Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e8032823d | ||
|
|
678fcd37e1 | ||
|
|
7530c59a94 | ||
|
|
6abab5c4e5 | ||
|
|
088de263cc | ||
|
|
ffc78a8814 | ||
|
|
3e88d62577 | ||
|
|
392044c658 | ||
|
|
9442a294f3 | ||
|
|
d149caafea | ||
|
|
000cb9b257 | ||
|
|
a5f0fb71e0 |
@@ -1,5 +1,5 @@
|
||||
```fix
|
||||
The Dakiny's admin team election
|
||||
Dakiny's Clan Council election
|
||||
```
|
||||
Dakiny is an organization built by and for its members. We are a community of people who come together to support each other in the creation of this magical and unique experience. To sustain what we've created here, and to grow and involve into the future, we vote in our leadership every 3 months to provide an opportunity for new clan members to represent their faction of Dakiny in the Clan Council.
|
||||
|
||||
@@ -14,10 +14,14 @@ Each quarter, a channel is created here on Discord for clan members to cast thei
|
||||
**Only clan members can vote and all clan members are granted exactly one equally weighted vote.** If you vote twice for different people, your vote will be annulled.
|
||||
|
||||
All clan members are permitted to run for a seat on the Clan Council as long as they:
|
||||
**i) **have been in the clan for enough time to have participated in at least one election;
|
||||
**ii)** have no warnings under their name
|
||||
and
|
||||
**iii)** have been active (hosted 1 clan event or participate in 1 clan event) in the clan in the past 12 weeks before current election.
|
||||
|
||||
**i)** have been in the clan for at least 13 weeks (enough time to have participated in at least one election);
|
||||
|
||||
**ii)** have no warnings under their name in the last 6 months and
|
||||
|
||||
**iii)** have been active in the clan for the past 13 weeks prior to the first day of elections. To be considered active in that timeframe, one must fulfill the following criteria: hosted/participated in at least three clan events, each one in a different week;
|
||||
|
||||
**iv)** are not running under aliases that hide their true identity.
|
||||
|
||||
Candidates are permitted to vote for themselves. Once a candidate reaches 3 votes from clan members, they have earned their seat, meaning no more votes can be cast for them.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Contains the election cycle class
|
||||
"""
|
||||
|
||||
import collections
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import nextcord
|
||||
from sqlalchemy import select
|
||||
@@ -140,6 +140,8 @@ class ElectionCycle:
|
||||
await self.election_start_action()
|
||||
elif today.date() == self.voting_start.date():
|
||||
await self.voting_start_action()
|
||||
elif (self.voting_end - timedelta(3)).date() <= today.date() < self.voting_end.date():
|
||||
await self.election_reminder()
|
||||
elif today.date() == self.voting_end.date():
|
||||
await self.voting_end_action()
|
||||
elif today.date() == self.highcouncil_election_start.date():
|
||||
@@ -273,3 +275,15 @@ class ElectionCycle:
|
||||
ElectionInformation.ElectionCycle == get_election_cycle_name(self.config)
|
||||
)
|
||||
return session.scalar(stmt)
|
||||
|
||||
async def election_reminder(self) -> None:
|
||||
"""
|
||||
Sends a message to general chat reminder clan about election.
|
||||
"""
|
||||
|
||||
general: nextcord.TextChannel = self.guild.get_channel(self.config.params.general_channel_id)
|
||||
|
||||
message = f"Reminder to <@&{self.config.params.clan_member_role_id}>! Clan elections are happening! Head over "
|
||||
message += f"to <#{self.get_election_info().ElectionChannel}> to cast your vote!"
|
||||
|
||||
await general.send(content=message)
|
||||
|
||||
@@ -11,7 +11,7 @@ from config import DakinyConfig
|
||||
from database_setup.sql_data_classes import CouncilMembers, engine
|
||||
from utils import get_election_cycle_name
|
||||
|
||||
NOMINEE_SPLITTER = "========================================================\n\n"
|
||||
NOMINEE_SPLITTER = "========================================\n\n"
|
||||
|
||||
|
||||
class NominateModal(nextcord.ui.Modal):
|
||||
@@ -69,7 +69,6 @@ class NominateModal(nextcord.ui.Modal):
|
||||
.where(CouncilMembers.MemberId == self.usr_id)
|
||||
)
|
||||
obj = session.scalar(stmt)
|
||||
obj.SpeechMsgContents = self.speech.value
|
||||
|
||||
msg: nextcord.Message = await interaction.channel.fetch_message(obj.SpeechMsgId)
|
||||
await msg.edit(content=decorate_nomination(interaction.user.id, self.speech.value))
|
||||
@@ -115,13 +114,13 @@ class NominateView(nextcord.ui.View):
|
||||
)
|
||||
|
||||
|
||||
def decorate_nomination(user_id, speech) -> str:
|
||||
def decorate_nomination(user_id: int, speech: str) -> str:
|
||||
"""
|
||||
Decorates a nomination speech so that it sits within multiple speeches better.
|
||||
|
||||
Args:
|
||||
user_id (_type_): ID of user sending the speech
|
||||
speech (_type_): Speech from the user
|
||||
user_id (int): ID of user sending the speech
|
||||
speech (str): Speech from the user
|
||||
|
||||
Returns:
|
||||
str: Decorated speech
|
||||
|
||||
@@ -7,6 +7,7 @@ from nextcord.ext import commands
|
||||
from cogs.commands.request import RequestCog
|
||||
from cogs.commands.set import setCog
|
||||
from cogs.commands.test import TestCog
|
||||
from cogs.commands.view import ViewCog
|
||||
|
||||
|
||||
# This function will be called when this extension is loaded.
|
||||
@@ -19,3 +20,4 @@ def setup(bot: commands.Bot):
|
||||
bot.add_cog(RequestCog(bot))
|
||||
bot.add_cog(TestCog(bot))
|
||||
bot.add_cog(setCog())
|
||||
bot.add_cog(ViewCog())
|
||||
|
||||
@@ -87,3 +87,15 @@ class TestCog(commands.Cog, name="Test commands."):
|
||||
config: DakinyConfig = self.bot.config
|
||||
cycle = ElectionCycle(get_current_cycle(config), config, self.bot.get_guild(config.params.guild_id))
|
||||
await cycle.voting_end_action()
|
||||
|
||||
@test.subcommand()
|
||||
async def election_reminder(self, interaction: nextcord.Interaction):
|
||||
config: DakinyConfig = self.bot.config
|
||||
cycle = ElectionCycle(get_current_cycle(config), config, self.bot.get_guild(config.params.guild_id))
|
||||
await cycle.election_reminder()
|
||||
|
||||
@test.subcommand()
|
||||
async def daily(self, interaction: nextcord.Interaction):
|
||||
config: DakinyConfig = self.bot.config
|
||||
cycle = ElectionCycle(get_current_cycle(config), config, self.bot.get_guild(config.params.guild_id))
|
||||
await cycle.check_date()
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
Commands for viewing information in the database.
|
||||
"""
|
||||
|
||||
import nextcord
|
||||
from nextcord.ext import commands
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from database_setup.sql_data_classes import ElectionInformation, Votes, engine
|
||||
|
||||
|
||||
class ViewCog(commands.Cog, name="View Commands"):
|
||||
"""
|
||||
Holds the view commands.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Default constructor for ViewCog class. Does nothing.
|
||||
"""
|
||||
|
||||
@nextcord.slash_command()
|
||||
async def view(self, interaction: nextcord.Interaction):
|
||||
"""
|
||||
Parent slash command for set. Never called.
|
||||
"""
|
||||
|
||||
@view.subcommand(
|
||||
description="Shows all voters and who they voted for in a given election."
|
||||
)
|
||||
async def votes(
|
||||
self,
|
||||
interaction: nextcord.Interaction,
|
||||
election_name: str = nextcord.SlashOption(
|
||||
required=True, description="The election you want to view votes for."
|
||||
),
|
||||
):
|
||||
"""
|
||||
Takes in an election name and sends a response message to the user with a list of people who voted in that
|
||||
election and who they voted for. Notes those tho abstained. Tells a user if the election is invalid. Uses
|
||||
autocompletion to assist with getting elections.
|
||||
|
||||
Args:
|
||||
interaction (nextcord.Interaction): User's interaction with the command.
|
||||
election_name (str): Is SlashOption. The name of the election to view votes for.
|
||||
"""
|
||||
|
||||
stmt = select(Votes).where(Votes.ElectionCycle == election_name)
|
||||
|
||||
with Session(engine) as session:
|
||||
# Check if election exists.
|
||||
if session.scalar(stmt) is None:
|
||||
await interaction.response.send_message(
|
||||
f"Invalid election: {election_name}.", ephemeral=True
|
||||
)
|
||||
return
|
||||
|
||||
votes = session.scalars(stmt)
|
||||
content = ""
|
||||
for vote in votes:
|
||||
content += (
|
||||
f"<@{vote.VoterId}> voted for <@{vote.VotedForId}>.\n"
|
||||
if vote.VotedForId is not None
|
||||
else f"<@{vote.VoterId}> abstained from voted.\n"
|
||||
)
|
||||
|
||||
await interaction.response.send_message(content=content, ephemeral=True)
|
||||
|
||||
@votes.on_autocomplete("election_name")
|
||||
async def election_autocomplete(
|
||||
self, interaction: nextcord.Interaction, election_name: str
|
||||
):
|
||||
"""
|
||||
Collects all elections that start with the election string.
|
||||
|
||||
Args:
|
||||
interaction (nextcord.Interaction): Interaction with command.
|
||||
election_name (str): String to run autocomplete on.
|
||||
"""
|
||||
|
||||
with Session(engine) as session:
|
||||
stmt = select(ElectionInformation.ElectionCycle)
|
||||
elections = session.scalars(stmt)
|
||||
|
||||
# if empty compare string
|
||||
if not election_name:
|
||||
await interaction.response.send_autocomplete(elections)
|
||||
return
|
||||
|
||||
autofilled_elections: "list[str]" = [
|
||||
election
|
||||
for election in elections
|
||||
if election.lower().startswith(election_name.lower())
|
||||
]
|
||||
await interaction.response.send_autocomplete(autofilled_elections[:25])
|
||||
Reference in New Issue
Block a user