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
102 changes: 89 additions & 13 deletions techsupport_bot/commands/voting.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import discord
import munch
import ui
import ui.persistent_voting
from core import cogs, extensionconfig
from discord import app_commands

Expand All @@ -40,13 +39,13 @@ async def setup(bot: bot.TechSupportBot) -> None:
default="",
)
config.add(
key="ping_role_id",
key="ping_role_ids",
datatype="str",
title="The role to ping when starting a vote",
title="The list of roles to ping when starting a vote",
description=(
"The role to ping when starting a vote, which will always be pinged"
"The list of roles to ping when starting a vote, which will always be pinged"
),
default="",
default=[],
)
await bot.add_cog(Voting(bot=bot, extension_name="voting"))
bot.add_extension_config("voting", config)
Expand Down Expand Up @@ -89,10 +88,10 @@ async def votingbutton(
int(config.extensions.voting.votes_channel_id.value)
)
roles = await interaction.guild.fetch_roles()
role = next(
role
roles_to_ping = " ".join(
role.mention
for role in roles
if role.id == int(config.extensions.voting.ping_role_id.value)
if str(role.id) in config.extensions.voting.ping_role_ids.value
)

vote = await self.bot.models.Votes(
Expand All @@ -111,7 +110,7 @@ async def votingbutton(
name=f"VOTE: {form.vote_short}",
allowed_mentions=discord.AllowedMentions(roles=True),
embed=embed,
content=role.mention,
content=roles_to_ping,
view=view,
)

Expand Down Expand Up @@ -185,14 +184,22 @@ async def build_vote_embed(
guild,
db_entry.vote_ids_yes.split(","),
db_entry.vote_ids_no.split(","),
db_entry.vote_ids_abstain.split(","),
(db_entry.vote_active and hide) or db_entry.anonymous,
),
)
print_yes_votes = "?" if (hide and db_entry.vote_active) else db_entry.votes_yes
print_no_votes = "?" if (hide and db_entry.vote_active) else db_entry.votes_no
print_abstain_votes = (
"?" if (hide and db_entry.vote_active) else db_entry.votes_abstain
)
embed.add_field(
name="Vote counts",
value=f"Votes for yes: {print_yes_votes}\nVotes for no: {print_no_votes}",
value=(
f"Votes for yes: {print_yes_votes}\n"
f"Votes for no: {print_no_votes}\n"
f"Votes to abstain: {print_abstain_votes}"
),
)
footer_str = f"Vote ID: {db_entry.vote_id}. "
if db_entry.blind:
Expand All @@ -207,6 +214,7 @@ async def make_fancy_voting_list(
guild: discord.Guild,
voters_yes: list[str],
voters_no: list[str],
voters_abstain: list[str],
should_hide: bool,
) -> str:
"""This makes a new line seperated string to be used in the "Votes" field
Expand All @@ -216,12 +224,13 @@ async def make_fancy_voting_list(
guild (discord.Guild): The guild this vote is taking place in
voters_yes (list[str]): The list of IDs of yes votes
voters_no (list[str]): The list of IDs of no votes
voters_abstain (list[str]): The list of IDs of abstian votes
should_hide (bool): Should who voted for what be hidden

Returns:
str: The prepared string, that respects blind/anonymous
"""
voters = voters_yes + voters_no
voters = voters_yes + voters_no + voters_abstain
final_str = []
for user in voters:
if len(user) == 0:
Expand All @@ -231,8 +240,10 @@ async def make_fancy_voting_list(
final_str.append(f"{user_object.display_name} - ?")
elif user in voters_yes:
final_str.append(f"{user_object.display_name} - yes")
else:
elif user in voters_no:
final_str.append(f"{user_object.display_name} - no")
else:
final_str.append(f"{user_object.display_name} - abstain")
final_str.sort()
return "\n".join(final_str)

Expand Down Expand Up @@ -275,6 +286,8 @@ async def register_yes_vote(
votes_no=db_entry.votes_no,
vote_ids_yes=db_entry.vote_ids_yes,
votes_yes=db_entry.votes_yes,
vote_ids_abstain=db_entry.vote_ids_abstain,
votes_abstain=db_entry.votes_abstain,
vote_ids_all=db_entry.vote_ids_all,
).apply()

Expand All @@ -284,6 +297,56 @@ async def register_yes_vote(
"Your vote for yes has been counted", ephemeral=True
)

async def register_abstain_vote(
self: Self,
interaction: discord.Interaction,
view: discord.ui.View,
) -> None:
"""This updates the vote database when someone votes to abstain

Args:
interaction (discord.Interaction): The interaction that started the vote
view (discord.ui.View): The view that was interacted with
"""
db_entry = await self.search_db_for_vote_by_message(str(interaction.message.id))

# Update vote_ids_abstain
vote_ids_abstain = db_entry.vote_ids_abstain.split(",")
if str(interaction.user.id) in vote_ids_abstain:
await interaction.response.send_message(
"You have already voted to abstian", ephemeral=True
)
return # Already voted to abstian, don't do anything more

db_entry = self.clear_vote_record(db_entry, str(interaction.user.id))

vote_ids_abstain.append(str(interaction.user.id))
db_entry.vote_ids_abstain = ",".join(vote_ids_abstain)

# Increment votes_abstian
db_entry.votes_abstain += 1

# Update vote_ids_all
vote_ids_all = db_entry.vote_ids_all.split(",")
vote_ids_all.append(str(interaction.user.id))
db_entry.vote_ids_all = ",".join(vote_ids_all)

await db_entry.update(
vote_ids_no=db_entry.vote_ids_no,
votes_no=db_entry.votes_no,
vote_ids_yes=db_entry.vote_ids_yes,
votes_yes=db_entry.votes_yes,
vote_ids_abstain=db_entry.vote_ids_abstain,
votes_abstain=db_entry.votes_abstain,
vote_ids_all=db_entry.vote_ids_all,
).apply()

embed = await self.build_vote_embed(db_entry.vote_id, interaction.guild)
await interaction.message.edit(embed=embed, view=view)
await interaction.response.send_message(
"Your vote to abstain has been counted", ephemeral=True
)

async def register_no_vote(
self: Self,
interaction: discord.Interaction,
Expand Down Expand Up @@ -323,6 +386,8 @@ async def register_no_vote(
votes_no=db_entry.votes_no,
vote_ids_yes=db_entry.vote_ids_yes,
votes_yes=db_entry.votes_yes,
vote_ids_abstain=db_entry.vote_ids_abstain,
votes_abstain=db_entry.votes_abstain,
vote_ids_all=db_entry.vote_ids_all,
).apply()

Expand Down Expand Up @@ -352,6 +417,8 @@ async def clear_vote(
votes_no=db_entry.votes_no,
vote_ids_yes=db_entry.vote_ids_yes,
votes_yes=db_entry.votes_yes,
vote_ids_abstain=db_entry.vote_ids_abstain,
votes_abstain=db_entry.votes_abstain,
vote_ids_all=db_entry.vote_ids_all,
).apply()

Expand Down Expand Up @@ -388,6 +455,13 @@ def clear_vote_record(
db_entry.votes_no -= 1
db_entry.vote_ids_no = ",".join(vote_ids_no)

# If there is a vote for abstain, remote it
vote_ids_abstain = db_entry.vote_ids_abstain.split(",")
if user_id in vote_ids_abstain:
vote_ids_abstain.remove(user_id)
db_entry.votes_abstain -= 1
db_entry.vote_ids_abstain = ",".join(vote_ids_abstain)

# Remove from vote id all
vote_ids_all = db_entry.vote_ids_all.split(",")
if user_id in vote_ids_all:
Expand Down Expand Up @@ -437,7 +511,9 @@ async def end_vote(self: Self, vote: munch.Munch, guild: discord.Guild) -> None:
embed = await self.build_vote_embed(vote.vote_id, guild)
# If the vote is anonymous, at this point we need to clear the vote record forever
if vote.anonymous:
await vote.update(vote_ids_yes="", vote_ids_no="").apply()
await vote.update(
vote_ids_yes="", vote_ids_no="", vote_ids_abstain=""
).apply()

channel = await guild.fetch_channel(int(vote.thread_id))
message = await channel.fetch_message(int(vote.message_id))
Expand Down
4 changes: 4 additions & 0 deletions techsupport_bot/core/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,11 @@ class Votes(bot.db.Model):
vote_description (str): The long form description of the vote
vote_ids_yes (str): The comma separated list of who has voted yes
vote_ids_no (str): The comma separated list of who has voted no
vote_ids_abstain (str): The comma separated list of who have abstained
vote_ids_all (str): The comma separated list of who has voted
votes_yes (int): The number of votes for yes
votes_no (int): The number of votes for no
votes_abstain (int): The number of votes that have abstained
votes_total (int): The number of votes
start_time (datetime.datetime): The start time of the vote
vote_active (bool): If the vote is current active or not
Expand All @@ -353,9 +355,11 @@ class Votes(bot.db.Model):
vote_description: str = bot.db.Column(bot.db.String)
vote_ids_yes: str = bot.db.Column(bot.db.String, default="")
vote_ids_no: str = bot.db.Column(bot.db.String, default="")
vote_ids_abstain: str = bot.db.Column(bot.db.String, default="")
vote_ids_all: str = bot.db.Column(bot.db.String, default="")
votes_yes: int = bot.db.Column(bot.db.Integer, default=0)
votes_no: int = bot.db.Column(bot.db.Integer, default=0)
votes_abstain: int = bot.db.Column(bot.db.Integer, default=0)
votes_total: int = bot.db.Column(bot.db.Integer, default=0)
start_time: datetime.datetime = bot.db.Column(
bot.db.DateTime, default=datetime.datetime.utcnow
Expand Down
23 changes: 22 additions & 1 deletion techsupport_bot/ui/persistent_voting.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,30 @@ async def yes_button(
cog = interaction.client.get_cog("Voting")
await cog.register_yes_vote(interaction, self)

@discord.ui.button(
label="Abstain from voting",
style=discord.ButtonStyle.blurple,
custom_id="persistent_voting_view:abstain",
row=1,
)
async def abstain_button(
self: Self, interaction: discord.Interaction, button: discord.ui.Button
) -> None:
"""The button that is for voting yes.
Calls the yes function in the main commands/voting.py file

Args:
interaction (discord.Interaction): The interaction created when the button was pressed
button (discord.ui.Button): The button object itself
"""
cog = interaction.client.get_cog("Voting")
await cog.register_abstain_vote(interaction, self)

@discord.ui.button(
label="No, don't make changes",
style=discord.ButtonStyle.red,
custom_id="persistent_voting_view:no",
row=0,
)
async def no_button(
self: Self, interaction: discord.Interaction, button: discord.ui.Button
Expand All @@ -50,9 +70,10 @@ async def no_button(
await cog.register_no_vote(interaction, self)

@discord.ui.button(
label="Clear vote",
label="Remove your vote",
style=discord.ButtonStyle.grey,
custom_id="persistent_voting_view:clear",
row=1,
)
async def clear_button(
self: Self, interaction: discord.Interaction, button: discord.ui.Button
Expand Down
Loading