Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f1e4ad20d | ||
|
|
354823ca1f | ||
|
|
45f17e09d3 | ||
|
|
530ca5b172 | ||
|
|
3dfdc10334 | ||
|
|
6c9353990c | ||
|
|
5412ab14a3 | ||
|
|
16cdc27bdb | ||
|
|
086f8b50cd | ||
|
|
a6a73cb46e | ||
|
|
57e19f3036 | ||
|
|
51c8483dad | ||
|
|
6b60c37726 | ||
|
|
3eea225931 | ||
|
|
0529dc6b4c | ||
|
|
865fee4ce2 | ||
|
|
052202d273 | ||
|
|
c09dd94284 | ||
|
|
76f4a9f37f | ||
|
|
f446f8e9cc | ||
|
|
dbd1d6dc00 | ||
|
|
66621ea2a0 | ||
|
|
6ae56bf8b2 | ||
|
|
f472c0419b | ||
|
|
a7532773c1 | ||
|
|
81ad4ba313 | ||
|
|
b221f1e438 | ||
|
|
1b08ba1355 | ||
|
|
e191df1a38 | ||
|
|
e35a2a8c8d | ||
|
|
d7046e4595 |
Vendored
+2
@@ -8,7 +8,9 @@
|
|||||||
"CLANLEADER",
|
"CLANLEADER",
|
||||||
"Dakiny",
|
"Dakiny",
|
||||||
"DISCORDMANAGER",
|
"DISCORDMANAGER",
|
||||||
|
"gitea",
|
||||||
"highcouncil",
|
"highcouncil",
|
||||||
|
"imcompiling",
|
||||||
"nextcord",
|
"nextcord",
|
||||||
"ondelete"
|
"ondelete"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Empty init file for classes module."""
|
||||||
+156
-96
@@ -5,17 +5,17 @@ from datetime import UTC, datetime, timedelta
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
|
from sqlalchemy import select, update
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlmodel import col
|
||||||
|
|
||||||
from dakiny_bot.classes.nominate_highcouncil_views import NominateHighCouncilView
|
from dakiny_bot.classes.nominate_highcouncil_views import NominateHighCouncilView
|
||||||
from dakiny_bot.classes.nominate_views import NominateView
|
from dakiny_bot.classes.nominate_views import NominateView
|
||||||
from dakiny_bot.classes.voting_highcouncil_views import VoteHighcouncilView
|
from dakiny_bot.classes.voting_highcouncil_views import VoteHighcouncilView
|
||||||
from dakiny_bot.classes.voting_views import VoteView
|
from dakiny_bot.classes.voting_views import VoteView
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.dakiny_settings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import (
|
from dakiny_bot.database.db import engine
|
||||||
CouncilMembers,
|
from dakiny_bot.database.models import CouncilMember, ElectionInformation, Vote
|
||||||
ElectionInformation,
|
|
||||||
Votes,
|
|
||||||
engine,
|
|
||||||
)
|
|
||||||
from dakiny_bot.logger import dakiny_logger
|
from dakiny_bot.logger import dakiny_logger
|
||||||
from dakiny_bot.utils import (
|
from dakiny_bot.utils import (
|
||||||
TEMPLATES_DIR,
|
TEMPLATES_DIR,
|
||||||
@@ -26,8 +26,6 @@ from dakiny_bot.utils import (
|
|||||||
get_datetime,
|
get_datetime,
|
||||||
get_election_name,
|
get_election_name,
|
||||||
)
|
)
|
||||||
from sqlalchemy import select, true, update
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
REQUIRED_VOTES_TO_BE_ELECTED = 3
|
REQUIRED_VOTES_TO_BE_ELECTED = 3
|
||||||
|
|
||||||
@@ -70,7 +68,7 @@ class Election:
|
|||||||
list[str]: A list of message contents in the correct order. Must be a string because the message is too
|
list[str]: A list of message contents in the correct order. Must be a string because the message is too
|
||||||
long.
|
long.
|
||||||
"""
|
"""
|
||||||
with Path(f"{TEMPLATES_DIR}/election_announcement.txt", encoding="utf8").open() as file:
|
with Path(f"{TEMPLATES_DIR}/election_announcement.txt").open() as file:
|
||||||
message = file.read()
|
message = file.read()
|
||||||
|
|
||||||
next_election = get_datetime(
|
next_election = get_datetime(
|
||||||
@@ -98,7 +96,7 @@ class Election:
|
|||||||
Returns:
|
Returns:
|
||||||
str: Message to send.
|
str: Message to send.
|
||||||
"""
|
"""
|
||||||
with Path(f"{TEMPLATES_DIR}/highcouncil_election.txt", encoding="utf8").open() as file:
|
with Path(f"{TEMPLATES_DIR}/highcouncil_election.txt").open() as file:
|
||||||
message = file.read()
|
message = file.read()
|
||||||
|
|
||||||
return message.format(
|
return message.format(
|
||||||
@@ -116,7 +114,7 @@ class Election:
|
|||||||
Returns:
|
Returns:
|
||||||
str: Message to send.
|
str: Message to send.
|
||||||
"""
|
"""
|
||||||
with Path(f"{TEMPLATES_DIR}/highcouncil_vote.txt", encoding="utf8").open() as file:
|
with Path(f"{TEMPLATES_DIR}/highcouncil_vote.txt").open() as file:
|
||||||
message = file.read()
|
message = file.read()
|
||||||
|
|
||||||
return message.format(
|
return message.format(
|
||||||
@@ -131,7 +129,7 @@ class Election:
|
|||||||
Returns:
|
Returns:
|
||||||
str: Message to send
|
str: Message to send
|
||||||
"""
|
"""
|
||||||
with Path(f"{TEMPLATES_DIR}/vote_start.txt", encoding="utf8").open() as file:
|
with Path(f"{TEMPLATES_DIR}/vote_start.txt").open() as file:
|
||||||
message = file.read()
|
message = file.read()
|
||||||
vote_end: datetime = get_datetime(settings.election_cycles[self.cycle].voting_end)
|
vote_end: datetime = get_datetime(settings.election_cycles[self.cycle].voting_end)
|
||||||
|
|
||||||
@@ -171,7 +169,10 @@ class Election:
|
|||||||
async def election_start_action(self) -> None:
|
async def election_start_action(self) -> None:
|
||||||
"""Performs required actions for when the election cycle starts."""
|
"""Performs required actions for when the election cycle starts."""
|
||||||
# create new channel for election
|
# create new channel for election
|
||||||
parent: nextcord.TextChannel = self.guild.get_channel(settings.channel_ids.voting)
|
parent = self.guild.get_channel(settings.channel_ids.voting)
|
||||||
|
if not isinstance(parent, nextcord.TextChannel):
|
||||||
|
dakiny_logger.error("Voting channel (id: %s) is not a text channel.", settings.channel_ids.voting)
|
||||||
|
return
|
||||||
quarter_name: str = get_election_name()
|
quarter_name: str = get_election_name()
|
||||||
|
|
||||||
thread_msg: nextcord.Message = await parent.send(content=quarter_name)
|
thread_msg: nextcord.Message = await parent.send(content=quarter_name)
|
||||||
@@ -192,9 +193,9 @@ class Election:
|
|||||||
# create new entry into database.
|
# create new entry into database.
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
new_cycle = ElectionInformation(
|
new_cycle = ElectionInformation(
|
||||||
ElectionName=quarter_name,
|
name=quarter_name,
|
||||||
ThreadId=thread.id,
|
thread_id=thread.id,
|
||||||
CouncilNominationMsgId=nominate_msg.id,
|
council_nomination_msg_id=nominate_msg.id,
|
||||||
)
|
)
|
||||||
session.add(new_cycle)
|
session.add(new_cycle)
|
||||||
session.commit()
|
session.commit()
|
||||||
@@ -207,64 +208,98 @@ class Election:
|
|||||||
Removes write permission from the general clan member role.
|
Removes write permission from the general clan member role.
|
||||||
"""
|
"""
|
||||||
message = self.build_vote_start()
|
message = self.build_vote_start()
|
||||||
election_thread: nextcord.Thread = self.guild.get_thread(self.get_election_info().ThreadId)
|
|
||||||
await election_thread.send(message)
|
|
||||||
vote_msg = await election_thread.send(
|
|
||||||
build_votes_str(self.guild),
|
|
||||||
view=VoteView(self.guild),
|
|
||||||
)
|
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = select(ElectionInformation).where(ElectionInformation.ElectionName == get_election_name())
|
stmt = select(ElectionInformation).where(col(ElectionInformation.name == get_election_name()))
|
||||||
info = session.scalar(stmt)
|
if (election_info := session.scalar(stmt)) is None:
|
||||||
info.CouncilVoteMsgId = vote_msg.id
|
dakiny_logger.error("Unable to find election %s", get_election_name())
|
||||||
|
return
|
||||||
|
if (election_thread := self.guild.get_thread(election_info.thread_id)) is None:
|
||||||
|
dakiny_logger.error("Recorded thread id %s does not result in a thread.", election_info.thread_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
await election_thread.send(message)
|
||||||
|
|
||||||
|
vote_msg = await election_thread.send(
|
||||||
|
build_votes_str(self.guild),
|
||||||
|
view=VoteView(self.guild),
|
||||||
|
)
|
||||||
|
|
||||||
|
election_info.council_vote_msg_id = vote_msg.id
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
thread: nextcord.Thread = self.guild.get_thread(info.ThreadId)
|
if election_info.council_nomination_msg_id is None:
|
||||||
nominate_msg: nextcord.Message = await thread.fetch_message(info.CouncilNominationMsgId)
|
dakiny_logger.error("Election doesn't have a nomination message set.")
|
||||||
|
return
|
||||||
|
|
||||||
|
nominate_msg: nextcord.Message = await election_thread.fetch_message(
|
||||||
|
election_info.council_nomination_msg_id
|
||||||
|
)
|
||||||
await nominate_msg.edit(view=None)
|
await nominate_msg.edit(view=None)
|
||||||
|
|
||||||
async def voting_end_action(self) -> None:
|
async def voting_end_action(self) -> None:
|
||||||
"""Performs required actions for when voting time has ended."""
|
"""Performs required actions for when voting time has ended."""
|
||||||
message = self.build_highcouncil_election_announce()
|
message = self.build_highcouncil_election_announce()
|
||||||
council_elect_role: nextcord.Role = self.guild.get_role(settings.role_ids.clan_council_elect)
|
if (council_elect_role := self.guild.get_role(settings.role_ids.clan_council_elect)) is None:
|
||||||
election_thread: nextcord.Thread = self.guild.get_thread(self.get_election_info().ThreadId)
|
dakiny_logger.error("Clan council elect role not found (id: %s).", settings.role_ids.clan_council_elect)
|
||||||
|
return
|
||||||
nominate_msg: nextcord.Message = await election_thread.send(message, view=NominateHighCouncilView(self.guild))
|
|
||||||
|
|
||||||
election_name = get_election_name()
|
election_name = get_election_name()
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
|
stmt = select(ElectionInformation).where(col(ElectionInformation.name == election_name))
|
||||||
|
if (info := session.scalar(stmt)) is None:
|
||||||
|
dakiny_logger.error("Unable to find election %s", election_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (election_thread := self.guild.get_thread(info.thread_id)) is None:
|
||||||
|
dakiny_logger.error("Recorded thread id %s does not result in a thread.", info.thread_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
nominate_msg: nextcord.Message = await election_thread.send(
|
||||||
|
message, view=NominateHighCouncilView(self.guild)
|
||||||
|
)
|
||||||
|
|
||||||
# remove vote button from vote message
|
# remove vote button from vote message
|
||||||
stmt = select(ElectionInformation).where(ElectionInformation.ElectionName == election_name)
|
if not info.council_vote_msg_id:
|
||||||
info = session.scalar(stmt)
|
dakiny_logger.error("Council vote message id not set.")
|
||||||
vote_msg: nextcord.Message = await election_thread.fetch_message(info.CouncilVoteMsgId)
|
return
|
||||||
|
vote_msg: nextcord.Message = await election_thread.fetch_message(info.council_vote_msg_id)
|
||||||
await vote_msg.edit(view=None)
|
await vote_msg.edit(view=None)
|
||||||
|
|
||||||
# add the highcouncil nomination message to election information
|
# add the highcouncil nomination message to election information
|
||||||
info.HighCouncilNominationMsgId = nominate_msg.id
|
info.high_council_nomination_msg_id = nominate_msg.id
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
# add the highcouncil role to everyone with 3 or more votes. set elected to true in database
|
# add the highcouncil role to everyone with 3 or more votes. set elected to true in database
|
||||||
stmt = (
|
stmt = (
|
||||||
select(Votes.VotedForId)
|
select(col(Vote.voted_for_id))
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.Type == VoteType.COUNCIL)
|
.where(col(Vote.type == VoteType.COUNCIL))
|
||||||
)
|
)
|
||||||
vote_count = collections.Counter(session.scalars(stmt))
|
vote_count = collections.Counter(session.scalars(stmt))
|
||||||
|
|
||||||
for user_id, votes in vote_count.items():
|
for user_id, votes in vote_count.items():
|
||||||
|
if user_id is None:
|
||||||
|
# Abstained votes, ignore
|
||||||
|
continue
|
||||||
if votes >= REQUIRED_VOTES_TO_BE_ELECTED:
|
if votes >= REQUIRED_VOTES_TO_BE_ELECTED:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers)
|
select(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == user_id)
|
.where(col(CouncilMember.member_id == user_id))
|
||||||
)
|
)
|
||||||
council_member = session.scalar(stmt)
|
if (council_member := session.scalar(stmt)) is None:
|
||||||
council_member.Elected = True
|
dakiny_logger.error(
|
||||||
|
"Council member (id: %s) for election %s not found.", user_id, election_name
|
||||||
|
)
|
||||||
|
return
|
||||||
|
council_member.elected = True
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
member: nextcord.Member = self.guild.get_member(user_id)
|
if (member := self.guild.get_member(user_id)) is None:
|
||||||
|
dakiny_logger.error("Member (id: %s) not found in guild.", user_id)
|
||||||
|
return
|
||||||
|
|
||||||
await member.add_roles(
|
await member.add_roles(
|
||||||
council_elect_role,
|
council_elect_role,
|
||||||
@@ -274,42 +309,58 @@ class Election:
|
|||||||
async def highcouncil_election_start_action(self) -> None:
|
async def highcouncil_election_start_action(self) -> None:
|
||||||
"""Performs required actions for when the highcouncil is elected."""
|
"""Performs required actions for when the highcouncil is elected."""
|
||||||
message = self.build_highcouncil_vote_start()
|
message = self.build_highcouncil_vote_start()
|
||||||
election_thread: nextcord.Thread = self.guild.get_thread(self.get_election_info().ThreadId)
|
|
||||||
await election_thread.send(message)
|
|
||||||
vote_msg = await election_thread.send(
|
|
||||||
build_highcouncil_votes_str(self.guild),
|
|
||||||
view=VoteHighcouncilView(self.guild),
|
|
||||||
)
|
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = select(ElectionInformation).where(ElectionInformation.ElectionName == get_election_name())
|
stmt = select(ElectionInformation).where(col(ElectionInformation.name == get_election_name()))
|
||||||
info = session.scalar(stmt)
|
if (info := session.scalar(stmt)) is None:
|
||||||
info.HighCouncilVoteMsgId = vote_msg.id
|
dakiny_logger.error("Unknown election name %s.", get_election_name())
|
||||||
|
return
|
||||||
|
|
||||||
|
if (election_thread := self.guild.get_thread(info.thread_id)) is None:
|
||||||
|
dakiny_logger.error("Recorded thread id %s does not result in a thread.", info.thread_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
await election_thread.send(message)
|
||||||
|
vote_msg = await election_thread.send(
|
||||||
|
build_highcouncil_votes_str(self.guild),
|
||||||
|
view=VoteHighcouncilView(self.guild),
|
||||||
|
)
|
||||||
|
|
||||||
|
info.high_council_vote_msg_id = vote_msg.id
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
# remove highcouncil nomination view
|
# remove highcouncil nomination view
|
||||||
nomination_msg: nextcord.Message = await election_thread.fetch_message(info.HighCouncilNominationMsgId)
|
if info.high_council_nomination_msg_id is None:
|
||||||
|
dakiny_logger.error("Election doesn't have a nomination message set.")
|
||||||
|
return
|
||||||
|
|
||||||
|
nomination_msg: nextcord.Message = await election_thread.fetch_message(info.high_council_nomination_msg_id)
|
||||||
await nomination_msg.edit(view=None)
|
await nomination_msg.edit(view=None)
|
||||||
|
|
||||||
async def term_start_action(self) -> None:
|
async def term_start_action(self) -> None:
|
||||||
"""Marks the election as over."""
|
"""Marks the election as over."""
|
||||||
self.election_over = True
|
self.election_over = True
|
||||||
|
|
||||||
elect_role: nextcord.Role = self.guild.get_role(settings.role_ids.clan_council_elect)
|
if (elect_role := self.guild.get_role(settings.role_ids.clan_council_elect)) is None:
|
||||||
council_role: nextcord.Role = self.guild.get_role(settings.role_ids.clan_council)
|
dakiny_logger.error("Cannot find Elect Role.")
|
||||||
|
return
|
||||||
|
if (council_role := self.guild.get_role(settings.role_ids.clan_council)) is None:
|
||||||
|
dakiny_logger.error("Cannot find Council Role.")
|
||||||
|
return
|
||||||
|
|
||||||
election_name = get_election_name()
|
election_name = get_election_name()
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
# role management
|
# role management
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers.MemberId)
|
select(col(CouncilMember.member_id))
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.Elected == true())
|
.where(col(CouncilMember.elected))
|
||||||
)
|
)
|
||||||
|
|
||||||
for member_id in session.scalars(stmt):
|
for member_id in session.scalars(stmt):
|
||||||
member = self.guild.get_member(member_id)
|
if (member := self.guild.get_member(member_id)) is None:
|
||||||
|
dakiny_logger.warning("Cannot find member (id: %s) to manage roles.", member_id)
|
||||||
|
continue
|
||||||
await member.remove_roles(
|
await member.remove_roles(
|
||||||
elect_role,
|
elect_role,
|
||||||
reason=f"{member.display_name} no longer needs the elect role.",
|
reason=f"{member.display_name} no longer needs the elect role.",
|
||||||
@@ -322,35 +373,36 @@ class Election:
|
|||||||
# announce high council members
|
# announce high council members
|
||||||
# ties are undefined behavior.
|
# ties are undefined behavior.
|
||||||
stmt = (
|
stmt = (
|
||||||
select(Votes.VotedForId)
|
select(col(Vote.voted_for_id))
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.Type == VoteType.DISCORDMANAGER)
|
.where(col(Vote.type == VoteType.DISCORDMANAGER))
|
||||||
)
|
)
|
||||||
dm_votes = collections.Counter(session.scalars(stmt))
|
dm_votes = collections.Counter(session.scalars(stmt))
|
||||||
stmt = (
|
stmt = (
|
||||||
select(Votes.VotedForId)
|
select(col(Vote.voted_for_id))
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.Type == VoteType.CLANLEADER)
|
.where(col(Vote.type == VoteType.CLANLEADER))
|
||||||
)
|
)
|
||||||
cl_votes = collections.Counter(session.scalars(stmt))
|
cl_votes = collections.Counter(session.scalars(stmt))
|
||||||
|
cl_votes.most_common()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dm_winner = max(dm_votes, key=dm_votes.get)
|
dm_winner = dm_votes.most_common(1).pop()[0]
|
||||||
stmt = (
|
stmt = (
|
||||||
update(CouncilMembers)
|
update(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == dm_winner)
|
.where(col(CouncilMember.member_id == dm_winner))
|
||||||
.values(DiscordManager=True)
|
.values(DiscordManager=True)
|
||||||
)
|
)
|
||||||
session.execute(stmt)
|
session.execute(stmt)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dm_winner = "No winner"
|
dm_winner = "No winner"
|
||||||
try:
|
try:
|
||||||
cl_winner = max(cl_votes, key=cl_votes.get)
|
cl_winner = cl_votes.most_common(1).pop()[0]
|
||||||
stmt = (
|
stmt = (
|
||||||
update(CouncilMembers)
|
update(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == cl_winner)
|
.where(col(CouncilMember.member_id == cl_winner))
|
||||||
.values(ClanLeader=True)
|
.values(ClanLeader=True)
|
||||||
)
|
)
|
||||||
session.execute(stmt)
|
session.execute(stmt)
|
||||||
@@ -359,14 +411,21 @@ class Election:
|
|||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
election_thread: nextcord.Thread = self.guild.get_thread(self.get_election_info().ThreadId)
|
stmt = select(ElectionInformation).where(col(ElectionInformation.name == election_name))
|
||||||
|
if (info := session.scalar(stmt)) is None:
|
||||||
|
dakiny_logger.error("Could not find information for this election (%s).", election_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (election_thread := self.guild.get_thread(info.thread_id)) is None:
|
||||||
|
dakiny_logger.error("Recorded thread id %s does not result in a thread.", info.thread_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if info.high_council_vote_msg_id is None:
|
||||||
|
dakiny_logger.error("No recorded High Council Voting Message.")
|
||||||
|
return
|
||||||
|
|
||||||
# clear view from vote message
|
# clear view from vote message
|
||||||
stmt = select(ElectionInformation.HighCouncilVoteMsgId).where(
|
vote_msg: nextcord.Message = await election_thread.fetch_message(info.high_council_vote_msg_id)
|
||||||
ElectionInformation.ElectionName == get_election_name()
|
|
||||||
)
|
|
||||||
vote_msg_id = session.scalar(stmt)
|
|
||||||
vote_msg: nextcord.Message = await election_thread.fetch_message(vote_msg_id)
|
|
||||||
await vote_msg.edit(view=None)
|
await vote_msg.edit(view=None)
|
||||||
|
|
||||||
highcouncil_msg: str = f"Congratulations for our high council members!\nClan Leader: <@{cl_winner}>\n"
|
highcouncil_msg: str = f"Congratulations for our high council members!\nClan Leader: <@{cl_winner}>\n"
|
||||||
@@ -378,22 +437,23 @@ class Election:
|
|||||||
await election_thread.edit(archived=True, locked=True)
|
await election_thread.edit(archived=True, locked=True)
|
||||||
await edit_clan_council_info(self.guild)
|
await edit_clan_council_info(self.guild)
|
||||||
|
|
||||||
def get_election_info(self) -> ElectionInformation:
|
|
||||||
"""
|
|
||||||
Gets information for current election.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ElectionInformation: The information from the database about the current election.
|
|
||||||
"""
|
|
||||||
with Session(engine) as session:
|
|
||||||
stmt = select(ElectionInformation).where(ElectionInformation.ElectionName == get_election_name())
|
|
||||||
return session.scalar(stmt)
|
|
||||||
|
|
||||||
async def election_reminder(self) -> None:
|
async def election_reminder(self) -> None:
|
||||||
"""Sends a message to general chat reminder clan about election."""
|
"""Sends a message to general chat reminder clan about election."""
|
||||||
general: nextcord.TextChannel = self.guild.get_channel(settings.channel_ids.general)
|
general = self.guild.get_channel(settings.channel_ids.general)
|
||||||
|
|
||||||
message = f"Reminder to <@&{settings.role_ids.clan_member}>! Clan elections are happening! Head over "
|
if not isinstance(general, nextcord.TextChannel):
|
||||||
message += f"to <#{self.get_election_info().ThreadId}> to cast your vote!"
|
dakiny_logger.error(
|
||||||
|
"Recorded general channel (id: %s) is not a text channel.", settings.channel_ids.general
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
await general.send(content=message)
|
with Session(engine) as session:
|
||||||
|
stmt = select(ElectionInformation).where(col(ElectionInformation.name == get_election_name()))
|
||||||
|
if (info := session.scalar(stmt)) is None:
|
||||||
|
dakiny_logger.error("Could not find election %s.", get_election_name())
|
||||||
|
return
|
||||||
|
|
||||||
|
message = f"Reminder to <@&{settings.role_ids.clan_member}>! Clan elections are happening! Head over "
|
||||||
|
message += f"to <#{info.thread_id}> to cast your vote!"
|
||||||
|
|
||||||
|
await general.send(content=message)
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
"""Contains various views to be used in the high council nomination process."""
|
"""Contains various views to be used in the high council nomination process."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
|
from nextcord.interactions import Interaction
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlmodel import col
|
||||||
|
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.dakiny_settings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import CouncilMembers, engine
|
from dakiny_bot.database.db import engine
|
||||||
|
from dakiny_bot.database.models import CouncilMember
|
||||||
|
from dakiny_bot.logger import dakiny_logger
|
||||||
from dakiny_bot.utils import (
|
from dakiny_bot.utils import (
|
||||||
NOMINEE_SPLITTER,
|
NOMINEE_SPLITTER,
|
||||||
decorate_cl_nomination,
|
decorate_cl_nomination,
|
||||||
decorate_dm_nomination,
|
decorate_dm_nomination,
|
||||||
get_election_name,
|
get_election_name,
|
||||||
)
|
)
|
||||||
from nextcord.interactions import Interaction
|
|
||||||
from sqlalchemy import select
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
|
|
||||||
class NominateDMModal(nextcord.ui.Modal):
|
class NominateDMModal(nextcord.ui.Modal):
|
||||||
@@ -52,26 +56,35 @@ class NominateDMModal(nextcord.ui.Modal):
|
|||||||
interaction (Interaction): Interaction with user.
|
interaction (Interaction): Interaction with user.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
speech = decorate_dm_nomination(interaction.user.id, self.speech.value)
|
if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
return
|
||||||
|
|
||||||
|
speech = decorate_dm_nomination(interaction.user.id, self.speech.value if self.speech.value else "")
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
# user already in table
|
# user already in table
|
||||||
election_name = get_election_name()
|
election_name = get_election_name()
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers)
|
select(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == self.usr_id)
|
.where(col(CouncilMember.member_id == self.usr_id))
|
||||||
)
|
)
|
||||||
council_elect = session.scalar(stmt)
|
council_elect = session.scalar(stmt)
|
||||||
|
|
||||||
if not self.usr_nominated:
|
if council_elect is None:
|
||||||
|
dakiny_logger.error("Did not find member (id: %s) in election %s.", self.usr_id, election_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.usr_nominated or council_elect.discord_manager_speech_id is None:
|
||||||
msg = await interaction.channel.send(speech)
|
msg = await interaction.channel.send(speech)
|
||||||
|
|
||||||
council_elect.DiscordManagerSpeechId = msg.id
|
council_elect.discord_manager_speech_id = msg.id
|
||||||
session.commit()
|
session.commit()
|
||||||
else:
|
else:
|
||||||
msg: nextcord.Message = await interaction.channel.fetch_message(council_elect.DiscordManagerSpeechId)
|
msg: nextcord.Message = await interaction.channel.fetch_message(council_elect.discord_manager_speech_id)
|
||||||
await msg.edit(content=decorate_dm_nomination(interaction.user.id, self.speech.value))
|
await msg.edit(content=speech)
|
||||||
|
|
||||||
|
|
||||||
class NominateCLModal(nextcord.ui.Modal):
|
class NominateCLModal(nextcord.ui.Modal):
|
||||||
@@ -112,26 +125,35 @@ class NominateCLModal(nextcord.ui.Modal):
|
|||||||
interaction (Interaction): Interaction with user.
|
interaction (Interaction): Interaction with user.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
speech = decorate_cl_nomination(interaction.user.id, self.speech.value)
|
if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
return
|
||||||
|
|
||||||
|
speech = decorate_cl_nomination(interaction.user.id, self.speech.value if self.speech.value else "")
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
# user already in table
|
# user already in table
|
||||||
election_name = get_election_name()
|
election_name = get_election_name()
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers)
|
select(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == self.usr_id)
|
.where(col(CouncilMember.member_id == self.usr_id))
|
||||||
)
|
)
|
||||||
council_elect = session.scalar(stmt)
|
council_elect = session.scalar(stmt)
|
||||||
|
|
||||||
if not self.usr_nominated:
|
if council_elect is None:
|
||||||
|
dakiny_logger.error("Did not find member (id: %s) in election %s.", self.usr_id, election_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.usr_nominated or council_elect.clan_leader_speech_id is None:
|
||||||
msg = await interaction.channel.send(speech)
|
msg = await interaction.channel.send(speech)
|
||||||
|
|
||||||
council_elect.ClanLeaderSpeechId = msg.id
|
council_elect.clan_leader_speech_id = msg.id
|
||||||
session.commit()
|
session.commit()
|
||||||
else:
|
else:
|
||||||
msg: nextcord.Message = await interaction.channel.fetch_message(council_elect.ClanLeaderSpeechId)
|
msg: nextcord.Message = await interaction.channel.fetch_message(council_elect.clan_leader_speech_id)
|
||||||
await msg.edit(content=decorate_cl_nomination(interaction.user.id, self.speech.value))
|
await msg.edit(content=speech)
|
||||||
|
|
||||||
|
|
||||||
class NominateHighCouncilView(nextcord.ui.View):
|
class NominateHighCouncilView(nextcord.ui.View):
|
||||||
@@ -157,8 +179,16 @@ class NominateHighCouncilView(nextcord.ui.View):
|
|||||||
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
||||||
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
||||||
"""
|
"""
|
||||||
|
if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
return
|
||||||
|
|
||||||
# enforce council elect only
|
# enforce council elect only
|
||||||
if self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles:
|
if (
|
||||||
|
isinstance(interaction.user, nextcord.User)
|
||||||
|
or self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles
|
||||||
|
):
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
f"You must have the <@&{settings.role_ids.clan_council_elect}> role to vote.",
|
f"You must have the <@&{settings.role_ids.clan_council_elect}> role to vote.",
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
@@ -170,16 +200,15 @@ class NominateHighCouncilView(nextcord.ui.View):
|
|||||||
speech_content = ""
|
speech_content = ""
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers)
|
select(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == interaction.user.id)
|
.where(col(CouncilMember.member_id == interaction.user.id))
|
||||||
)
|
)
|
||||||
obj = session.scalar(stmt)
|
council_elect = session.scalar(stmt)
|
||||||
|
|
||||||
# user needs to exist to get here anyways...
|
if council_elect is not None and council_elect.discord_manager_speech_id is not None:
|
||||||
if obj.DiscordManagerSpeechId is not None:
|
|
||||||
usr_nominated = True
|
usr_nominated = True
|
||||||
msg = await interaction.channel.fetch_message(obj.DiscordManagerSpeechId)
|
msg = await interaction.channel.fetch_message(council_elect.discord_manager_speech_id)
|
||||||
speech_content = msg.content.split(NOMINEE_SPLITTER)[1]
|
speech_content = msg.content.split(NOMINEE_SPLITTER)[1]
|
||||||
|
|
||||||
await interaction.response.send_modal(NominateDMModal(interaction.user.id, usr_nominated, speech_content))
|
await interaction.response.send_modal(NominateDMModal(interaction.user.id, usr_nominated, speech_content))
|
||||||
@@ -200,8 +229,16 @@ class NominateHighCouncilView(nextcord.ui.View):
|
|||||||
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
||||||
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
||||||
"""
|
"""
|
||||||
|
if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
return
|
||||||
|
|
||||||
# enforce council elect only
|
# enforce council elect only
|
||||||
if self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles:
|
if (
|
||||||
|
isinstance(interaction.user, nextcord.User)
|
||||||
|
or self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles
|
||||||
|
):
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
f"You must have the <@&{settings.role_ids.clan_council_elect}> role to vote.",
|
f"You must have the <@&{settings.role_ids.clan_council_elect}> role to vote.",
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
@@ -213,16 +250,15 @@ class NominateHighCouncilView(nextcord.ui.View):
|
|||||||
speech_content = ""
|
speech_content = ""
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers)
|
select(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == interaction.user.id)
|
.where(col(CouncilMember.member_id == interaction.user.id))
|
||||||
)
|
)
|
||||||
obj = session.scalar(stmt)
|
council_elect = session.scalar(stmt)
|
||||||
|
|
||||||
# user needs to exist to get here anyways...
|
if council_elect is not None and council_elect.clan_leader_speech_id is not None:
|
||||||
if obj.ClanLeaderSpeechId is not None:
|
|
||||||
usr_nominated = True
|
usr_nominated = True
|
||||||
msg = await interaction.channel.fetch_message(obj.ClanLeaderSpeechId)
|
msg = await interaction.channel.fetch_message(council_elect.clan_leader_speech_id)
|
||||||
speech_content = msg.content.split(NOMINEE_SPLITTER)[1]
|
speech_content = msg.content.split(NOMINEE_SPLITTER)[1]
|
||||||
|
|
||||||
await interaction.response.send_modal(NominateCLModal(interaction.user.id, usr_nominated, speech_content))
|
await interaction.response.send_modal(NominateCLModal(interaction.user.id, usr_nominated, speech_content))
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
"""Contains various views to be used in the nomination process."""
|
"""Contains various views to be used in the nomination process."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from dakiny_bot.dakiny_settings import settings
|
|
||||||
from dakiny_bot.database.sql_data_classes import CouncilMembers, engine
|
|
||||||
from dakiny_bot.utils import NOMINEE_SPLITTER, decorate_nomination, get_election_name
|
|
||||||
from nextcord.interactions import Interaction
|
from nextcord.interactions import Interaction
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlmodel import col
|
||||||
|
|
||||||
|
from dakiny_bot.dakiny_settings import settings
|
||||||
|
from dakiny_bot.database.db import engine
|
||||||
|
from dakiny_bot.database.models import CouncilMember
|
||||||
|
from dakiny_bot.logger import dakiny_logger
|
||||||
|
from dakiny_bot.utils import NOMINEE_SPLITTER, decorate_nomination, get_election_name
|
||||||
|
|
||||||
|
|
||||||
class NominateModal(nextcord.ui.Modal):
|
class NominateModal(nextcord.ui.Modal):
|
||||||
@@ -47,31 +51,45 @@ class NominateModal(nextcord.ui.Modal):
|
|||||||
interaction (Interaction): Interaction with user.
|
interaction (Interaction): Interaction with user.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
speech = decorate_nomination(interaction.user.id, self.speech.value)
|
if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
return
|
||||||
|
|
||||||
|
speech = decorate_nomination(interaction.user.id, self.speech.value if self.speech.value else "")
|
||||||
election_name = get_election_name()
|
election_name = get_election_name()
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
if not self.usr_nominated:
|
if not self.usr_nominated:
|
||||||
msg = await interaction.channel.send(speech)
|
msg = await interaction.channel.send(speech)
|
||||||
|
|
||||||
council_member = CouncilMembers(
|
council_member = CouncilMember(
|
||||||
ElectionName=election_name,
|
election_name=election_name,
|
||||||
MemberId=interaction.user.id,
|
member_id=interaction.user.id,
|
||||||
SpeechMsgId=msg.id,
|
speech_msg_id=msg.id,
|
||||||
)
|
)
|
||||||
session.add(council_member)
|
session.add(council_member)
|
||||||
session.commit()
|
session.commit()
|
||||||
else:
|
else:
|
||||||
# user already nominated, just need to update the election speech
|
# user already nominated, just need to update the election speech
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers)
|
select(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == self.usr_id)
|
.where(col(CouncilMember.member_id == self.usr_id))
|
||||||
)
|
)
|
||||||
obj = session.scalar(stmt)
|
council_elect = session.scalar(stmt)
|
||||||
|
|
||||||
msg: nextcord.Message = await interaction.channel.fetch_message(obj.SpeechMsgId)
|
if council_elect is None:
|
||||||
await msg.edit(content=decorate_nomination(interaction.user.id, self.speech.value))
|
dakiny_logger.error("Did not find member (id: %s) in election %s.", self.usr_id, election_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
if council_elect.speech_msg_id is None:
|
||||||
|
msg = await interaction.channel.send(speech)
|
||||||
|
council_elect.speech_msg_id = msg.id
|
||||||
|
session.commit()
|
||||||
|
else:
|
||||||
|
msg: nextcord.Message = await interaction.channel.fetch_message(council_elect.speech_msg_id)
|
||||||
|
await msg.edit(content=speech)
|
||||||
|
|
||||||
|
|
||||||
class NominateView(nextcord.ui.View):
|
class NominateView(nextcord.ui.View):
|
||||||
@@ -97,8 +115,16 @@ class NominateView(nextcord.ui.View):
|
|||||||
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
||||||
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
||||||
"""
|
"""
|
||||||
|
if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
return
|
||||||
|
|
||||||
# enforce members only
|
# enforce members only
|
||||||
if self.guild.get_role(settings.role_ids.clan_member) not in interaction.user.roles:
|
if (
|
||||||
|
isinstance(interaction.user, nextcord.User)
|
||||||
|
or self.guild.get_role(settings.role_ids.clan_member) not in interaction.user.roles
|
||||||
|
):
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
f"You must have the <@&{settings.role_ids.clan_member}> role to vote.",
|
f"You must have the <@&{settings.role_ids.clan_member}> role to vote.",
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
@@ -110,14 +136,14 @@ class NominateView(nextcord.ui.View):
|
|||||||
speech_content = ""
|
speech_content = ""
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers)
|
select(CouncilMember)
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.where(CouncilMembers.MemberId == interaction.user.id)
|
.where(col(CouncilMember.member_id == interaction.user.id))
|
||||||
)
|
)
|
||||||
obj = session.scalar(stmt)
|
obj = session.scalar(stmt)
|
||||||
if obj is not None:
|
if obj is not None and obj.speech_msg_id is not None:
|
||||||
usr_nominated = True
|
usr_nominated = True
|
||||||
msg = await interaction.channel.fetch_message(obj.SpeechMsgId)
|
msg = await interaction.channel.fetch_message(obj.speech_msg_id)
|
||||||
speech_content = msg.content.split(NOMINEE_SPLITTER)[1]
|
speech_content = msg.content.split(NOMINEE_SPLITTER)[1]
|
||||||
|
|
||||||
await interaction.response.send_modal(NominateModal(interaction.user.id, usr_nominated, speech_content))
|
await interaction.response.send_modal(NominateModal(interaction.user.id, usr_nominated, speech_content))
|
||||||
|
|||||||
@@ -4,7 +4,3 @@ extend = "../../pyproject.toml"
|
|||||||
|
|
||||||
# Exclude a variety of commonly ignored directories.
|
# Exclude a variety of commonly ignored directories.
|
||||||
exclude = ["gitea_webhook.py"]
|
exclude = ["gitea_webhook.py"]
|
||||||
|
|
||||||
[tool.ruff.lint]
|
|
||||||
# ignore implicit-namespace-package in this directory.
|
|
||||||
ignore = ["INP001"]
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
"""Contains various views to be used while voting for high council. Controls the logic when performing the vote."""
|
"""Contains various views to be used while voting for high council. Controls the logic when performing the vote."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from dakiny_bot.dakiny_settings import settings
|
|
||||||
from dakiny_bot.database.sql_data_classes import (
|
|
||||||
CouncilMembers,
|
|
||||||
ElectionInformation,
|
|
||||||
Votes,
|
|
||||||
engine,
|
|
||||||
)
|
|
||||||
from dakiny_bot.utils import VoteType, build_highcouncil_votes_str, get_election_name
|
|
||||||
from nextcord import SelectOption
|
from nextcord import SelectOption
|
||||||
from nextcord.interactions import Interaction
|
from nextcord.interactions import Interaction
|
||||||
from sqlalchemy import exc, select, update
|
from sqlalchemy import exc, select, update
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlmodel import col
|
||||||
|
|
||||||
|
from dakiny_bot.dakiny_settings import settings
|
||||||
|
from dakiny_bot.database.db import engine
|
||||||
|
from dakiny_bot.database.models import CouncilMember, ElectionInformation, Vote
|
||||||
|
from dakiny_bot.logger import dakiny_logger
|
||||||
|
from dakiny_bot.utils import VoteType, build_highcouncil_votes_str, get_election_name
|
||||||
|
|
||||||
|
|
||||||
class VoteSelect(nextcord.ui.StringSelect):
|
class VoteSelect(nextcord.ui.StringSelect):
|
||||||
@@ -37,20 +36,32 @@ class VoteSelect(nextcord.ui.StringSelect):
|
|||||||
Args:
|
Args:
|
||||||
interaction (Interaction): Interaction with this callback.
|
interaction (Interaction): Interaction with this callback.
|
||||||
"""
|
"""
|
||||||
|
if (
|
||||||
|
interaction.user is None
|
||||||
|
or not isinstance(interaction.channel, nextcord.TextChannel)
|
||||||
|
or interaction.guild is None
|
||||||
|
):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
# if no guild, is problem because should only be in guild.
|
||||||
|
return
|
||||||
|
|
||||||
election_name: str = get_election_name()
|
election_name: str = get_election_name()
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = select(ElectionInformation.HighCouncilVoteMsgId).where(
|
stmt = select(col(ElectionInformation.high_council_vote_msg_id)).where(
|
||||||
ElectionInformation.ElectionName == election_name
|
col(ElectionInformation.name == election_name)
|
||||||
)
|
)
|
||||||
vote_msg_id = session.scalar(stmt)
|
if (vote_msg_id := session.scalar(stmt)) is None:
|
||||||
|
dakiny_logger.error("Unable to find a Highcouncil Vote Message ID in database.")
|
||||||
|
return
|
||||||
|
|
||||||
voted_for = None if self.values[0] == "Abstain" else int(self.values[0])
|
voted_for = None if self.values[0] == "Abstain" else int(self.values[0])
|
||||||
|
|
||||||
new_vote = Votes(
|
new_vote = Vote(
|
||||||
ElectionName=election_name,
|
election_name=election_name,
|
||||||
VoterId=interaction.user.id,
|
voter_id=interaction.user.id,
|
||||||
VotedForId=voted_for,
|
voted_for_id=voted_for,
|
||||||
Type=self.election_type,
|
type=self.election_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -61,10 +72,10 @@ class VoteSelect(nextcord.ui.StringSelect):
|
|||||||
# user already has a vote in the election and election type
|
# user already has a vote in the election and election type
|
||||||
session.rollback()
|
session.rollback()
|
||||||
stmt = (
|
stmt = (
|
||||||
update(Votes)
|
update(Vote)
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.VoterId == interaction.user.id)
|
.where(col(Vote.voter_id == interaction.user.id))
|
||||||
.where(Votes.Type == self.election_type)
|
.where(col(Vote.type == self.election_type))
|
||||||
.values(VotedForId=voted_for)
|
.values(VotedForId=voted_for)
|
||||||
)
|
)
|
||||||
session.execute(stmt)
|
session.execute(stmt)
|
||||||
@@ -103,6 +114,10 @@ class VoteHighcouncilView(nextcord.ui.View):
|
|||||||
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
||||||
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(interaction.user, nextcord.Member):
|
||||||
|
# the user should only be a member of the guild.
|
||||||
|
return
|
||||||
|
|
||||||
# enforce council elect only
|
# enforce council elect only
|
||||||
if self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles:
|
if self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
@@ -115,32 +130,29 @@ class VoteHighcouncilView(nextcord.ui.View):
|
|||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(Votes.VotedForId)
|
select(col(Vote.voted_for_id))
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.VoterId == interaction.user.id)
|
.where(col(Vote.voter_id == interaction.user.id))
|
||||||
.where(Votes.Type == VoteType.CLANLEADER)
|
.where(col(Vote.type == VoteType.CLANLEADER))
|
||||||
)
|
)
|
||||||
voted_for = session.scalar(stmt)
|
voted_for = session.scalar(stmt)
|
||||||
|
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers.MemberId)
|
select(col(CouncilMember.member_id))
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.filter(CouncilMembers.ClanLeaderSpeechId.is_not(None))
|
.filter(col(CouncilMember.clan_leader_speech_id is not None))
|
||||||
)
|
)
|
||||||
|
|
||||||
options = [
|
options = [
|
||||||
SelectOption(
|
SelectOption(
|
||||||
label=(
|
label=(f"{member.display_name} ☑️" if voted_for == nominee else f"{member.display_name}"),
|
||||||
f"{self.guild.get_member(nominee).display_name} ☑️"
|
|
||||||
if voted_for == nominee
|
|
||||||
else f"{self.guild.get_member(nominee).display_name}"
|
|
||||||
),
|
|
||||||
value=str(nominee),
|
value=str(nominee),
|
||||||
)
|
)
|
||||||
for nominee in sorted(
|
for nominee in sorted(
|
||||||
session.scalars(stmt),
|
session.scalars(stmt),
|
||||||
key=lambda nominee: self.guild.get_member(nominee).display_name.lower(),
|
key=lambda nominee: self.guild.get_member(nominee).display_name.lower(), # pyright: ignore[reportOptionalMemberAccess]
|
||||||
)
|
)
|
||||||
|
if (member := self.guild.get_member(nominee)) is not None
|
||||||
]
|
]
|
||||||
options.append(
|
options.append(
|
||||||
SelectOption(
|
SelectOption(
|
||||||
@@ -175,6 +187,10 @@ class VoteHighcouncilView(nextcord.ui.View):
|
|||||||
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
||||||
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(interaction.user, nextcord.Member):
|
||||||
|
# the user should only be a member of the guild.
|
||||||
|
return
|
||||||
|
|
||||||
# enforce council elect only
|
# enforce council elect only
|
||||||
if self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles:
|
if self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
@@ -187,32 +203,29 @@ class VoteHighcouncilView(nextcord.ui.View):
|
|||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(Votes.VotedForId)
|
select(col(Vote.voted_for_id))
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.VoterId == interaction.user.id)
|
.where(col(Vote.voter_id == interaction.user.id))
|
||||||
.where(Votes.Type == VoteType.DISCORDMANAGER)
|
.where(col(Vote.type == VoteType.DISCORDMANAGER))
|
||||||
)
|
)
|
||||||
voted_for = session.scalar(stmt)
|
voted_for = session.scalar(stmt)
|
||||||
|
|
||||||
stmt = (
|
stmt = (
|
||||||
select(CouncilMembers.MemberId)
|
select(col(CouncilMember.member_id))
|
||||||
.where(CouncilMembers.ElectionName == election_name)
|
.where(col(CouncilMember.election_name == election_name))
|
||||||
.filter(CouncilMembers.DiscordManagerSpeechId.is_not(None))
|
.filter(col(CouncilMember.discord_manager_speech_id is not None))
|
||||||
)
|
)
|
||||||
|
|
||||||
options = [
|
options = [
|
||||||
SelectOption(
|
SelectOption(
|
||||||
label=(
|
label=(f"{member.display_name} ☑️" if voted_for == nominee else f"{member.display_name}"),
|
||||||
f"{self.guild.get_member(nominee).display_name} ☑️"
|
|
||||||
if voted_for == nominee
|
|
||||||
else f"{self.guild.get_member(nominee).display_name}"
|
|
||||||
),
|
|
||||||
value=str(nominee),
|
value=str(nominee),
|
||||||
)
|
)
|
||||||
for nominee in sorted(
|
for nominee in sorted(
|
||||||
session.scalars(stmt),
|
session.scalars(stmt),
|
||||||
key=lambda nominee: self.guild.get_member(nominee).display_name.lower(),
|
key=lambda nominee: self.guild.get_member(nominee).display_name.lower(), # pyright: ignore[reportOptionalMemberAccess]
|
||||||
)
|
)
|
||||||
|
if (member := self.guild.get_member(nominee)) is not None
|
||||||
]
|
]
|
||||||
options.append(
|
options.append(
|
||||||
SelectOption(
|
SelectOption(
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
"""Contains various views to be used while voting. Controls the logic when performing the vote."""
|
"""Contains various views to be used while voting. Controls the logic when performing the vote."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from dakiny_bot.dakiny_settings import settings
|
|
||||||
from dakiny_bot.database.sql_data_classes import (
|
|
||||||
CouncilMembers,
|
|
||||||
ElectionInformation,
|
|
||||||
Votes,
|
|
||||||
engine,
|
|
||||||
)
|
|
||||||
from dakiny_bot.utils import VoteType, build_votes_str, get_election_name
|
|
||||||
from nextcord import SelectOption
|
from nextcord import SelectOption
|
||||||
from nextcord.interactions import Interaction
|
from nextcord.interactions import Interaction
|
||||||
from sqlalchemy import exc, select, update
|
from sqlalchemy import exc, select, update
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlmodel import col
|
||||||
|
|
||||||
|
from dakiny_bot.dakiny_settings import settings
|
||||||
|
from dakiny_bot.database.db import engine
|
||||||
|
from dakiny_bot.database.models import CouncilMember, ElectionInformation, Vote
|
||||||
|
from dakiny_bot.logger import dakiny_logger
|
||||||
|
from dakiny_bot.utils import VoteType, build_votes_str, get_election_name
|
||||||
|
|
||||||
|
|
||||||
class VoteSelect(nextcord.ui.StringSelect):
|
class VoteSelect(nextcord.ui.StringSelect):
|
||||||
@@ -36,18 +35,32 @@ class VoteSelect(nextcord.ui.StringSelect):
|
|||||||
Args:
|
Args:
|
||||||
interaction (Interaction): Interaction with this callback.
|
interaction (Interaction): Interaction with this callback.
|
||||||
"""
|
"""
|
||||||
|
if (
|
||||||
|
interaction.user is None
|
||||||
|
or not isinstance(interaction.channel, nextcord.TextChannel)
|
||||||
|
or interaction.guild is None
|
||||||
|
):
|
||||||
|
# ignore none... Don't know what situation this is.
|
||||||
|
# if not textchannel, don't know how callback happened
|
||||||
|
# if no guild, is problem because should only be in guild.
|
||||||
|
return
|
||||||
|
|
||||||
election_name = get_election_name()
|
election_name = get_election_name()
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = select(ElectionInformation.CouncilVoteMsgId).where(ElectionInformation.ElectionName == election_name)
|
stmt = select(col(ElectionInformation.council_vote_msg_id)).where(
|
||||||
vote_msg_id = session.scalar(stmt)
|
col(ElectionInformation.name == election_name)
|
||||||
|
)
|
||||||
|
if (vote_msg_id := session.scalar(stmt)) is None:
|
||||||
|
dakiny_logger.error("Unable to find a Vote Message ID in database.")
|
||||||
|
return
|
||||||
|
|
||||||
voted_for = None if self.values[0] == "Abstain" else int(self.values[0])
|
voted_for = None if self.values[0] == "Abstain" else int(self.values[0])
|
||||||
|
|
||||||
new_vote = Votes(
|
new_vote = Vote(
|
||||||
ElectionName=election_name,
|
election_name=election_name,
|
||||||
VoterId=interaction.user.id,
|
voter_id=interaction.user.id,
|
||||||
VotedForId=voted_for,
|
voted_for_id=voted_for,
|
||||||
Type=VoteType.COUNCIL,
|
type=VoteType.COUNCIL,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -57,10 +70,10 @@ class VoteSelect(nextcord.ui.StringSelect):
|
|||||||
except exc.IntegrityError:
|
except exc.IntegrityError:
|
||||||
session.rollback()
|
session.rollback()
|
||||||
stmt = (
|
stmt = (
|
||||||
update(Votes)
|
update(Vote)
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.VoterId == interaction.user.id)
|
.where(col(Vote.voter_id == interaction.user.id))
|
||||||
.where(Votes.Type == VoteType.COUNCIL)
|
.where(col(Vote.type == VoteType.COUNCIL))
|
||||||
.values(VotedForId=voted_for)
|
.values(VotedForId=voted_for)
|
||||||
)
|
)
|
||||||
session.execute(stmt)
|
session.execute(stmt)
|
||||||
@@ -94,6 +107,10 @@ class VoteView(nextcord.ui.View):
|
|||||||
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
button (nextcord.ui.Button): Unused. idk why its needed in the decorator
|
||||||
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
interaction (nextcord.Interaction): Interaction of user who pressed the button
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(interaction.user, nextcord.Member):
|
||||||
|
# the user should only be a member of the guild.
|
||||||
|
return
|
||||||
|
|
||||||
# enforce members only
|
# enforce members only
|
||||||
if self.guild.get_role(settings.role_ids.clan_member) not in interaction.user.roles:
|
if self.guild.get_role(settings.role_ids.clan_member) not in interaction.user.roles:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
@@ -106,28 +123,25 @@ class VoteView(nextcord.ui.View):
|
|||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(Votes.VotedForId)
|
select(col(Vote.voted_for_id))
|
||||||
.where(Votes.ElectionName == election_name)
|
.where(col(Vote.election_name == election_name))
|
||||||
.where(Votes.VoterId == interaction.user.id)
|
.where(col(Vote.voter_id == interaction.user.id))
|
||||||
.where(Votes.Type == VoteType.COUNCIL)
|
.where(col(Vote.type == VoteType.COUNCIL))
|
||||||
)
|
)
|
||||||
voted_for = session.scalar(stmt)
|
voted_for = session.scalar(stmt)
|
||||||
|
|
||||||
stmt = select(CouncilMembers.MemberId).where(CouncilMembers.ElectionName == election_name)
|
stmt = select(col(CouncilMember.member_id)).where(col(CouncilMember.election_name == election_name))
|
||||||
|
|
||||||
options = [
|
options = [
|
||||||
SelectOption(
|
SelectOption(
|
||||||
label=(
|
label=(f"{member.display_name} ☑️" if voted_for == nominee else f"{member.display_name}"),
|
||||||
f"{self.guild.get_member(nominee).display_name} ☑️"
|
|
||||||
if voted_for == nominee
|
|
||||||
else f"{self.guild.get_member(nominee).display_name}"
|
|
||||||
),
|
|
||||||
value=str(nominee),
|
value=str(nominee),
|
||||||
)
|
)
|
||||||
for nominee in sorted(
|
for nominee in sorted(
|
||||||
session.scalars(stmt),
|
session.scalars(stmt),
|
||||||
key=lambda nominee: self.guild.get_member(nominee).display_name.lower(),
|
key=lambda nominee: self.guild.get_member(nominee).display_name.lower(), # pyright: ignore[reportOptionalMemberAccess]
|
||||||
)
|
)
|
||||||
|
if (member := self.guild.get_member(nominee)) is not None
|
||||||
]
|
]
|
||||||
options.append(
|
options.append(
|
||||||
SelectOption(
|
SelectOption(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import nextcord
|
|||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
|
|
||||||
from dakiny_bot.app import birthday_list
|
from dakiny_bot.app import birthday_list
|
||||||
|
from dakiny_bot.dakiny_settings import settings
|
||||||
|
|
||||||
|
|
||||||
class SubmissionListener(commands.Cog, name="Message Listener"):
|
class SubmissionListener(commands.Cog, name="Message Listener"):
|
||||||
@@ -16,7 +17,15 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
|
|||||||
async def on_message(self, message: nextcord.Message) -> None:
|
async def on_message(self, message: nextcord.Message) -> None:
|
||||||
"""Listener for message creation. Adds score and attachments to submission."""
|
"""Listener for message creation. Adds score and attachments to submission."""
|
||||||
# don't want to process any messages from bot.
|
# don't want to process any messages from bot.
|
||||||
if message.author.id == self.bot.user.id:
|
if message.author.id == self.bot.user.id: # pyright: ignore[reportOptionalMemberAccess]
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ban all members who message in this channel. Everyone in guild is a member, so shouldn't miss a ban
|
||||||
|
# due to message.author not being a nextcord.Member object.
|
||||||
|
if message.channel.id == settings.channel_ids.ban and isinstance(message.author, nextcord.Member):
|
||||||
|
await message.author.ban(
|
||||||
|
delete_message_seconds=30 * 60, reason="Probable spam. The ban channel was messaged."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
await birthday_action(message)
|
await birthday_action(message)
|
||||||
@@ -31,7 +40,7 @@ def setup(bot: commands.Bot) -> None:
|
|||||||
|
|
||||||
async def birthday_action(message: nextcord.Message) -> None:
|
async def birthday_action(message: nextcord.Message) -> None:
|
||||||
"""
|
"""
|
||||||
Checks a message's auther and mentions to see if they are in the birthday list.
|
Checks a message's author and mentions to see if they are in the birthday list.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message (nextcord.Message): The message to check for a birthday person.
|
message (nextcord.Message): The message to check for a birthday person.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class ChannelIds:
|
|||||||
event_post: int
|
event_post: int
|
||||||
general: int
|
general: int
|
||||||
voting: int
|
voting: int
|
||||||
|
ban: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
Reference in New Issue
Block a user