Compare commits

...
10 changed files with 128 additions and 27 deletions
+1
View File
@@ -139,3 +139,4 @@ TODO.txt
sql_tester.py
conf
outputs
dakiny.db
+51 -25
View File
@@ -6,8 +6,11 @@ from datetime import datetime, timedelta
import nextcord
from nextcord.ext import commands
from sqlalchemy import select
from sqlalchemy.orm import Session
from config import DakinyConfig
from database.sql_data_classes import ElectionInformation, engine
from utils import get_datetime
@@ -17,7 +20,7 @@ class ElectionCycle:
the cycle.
"""
def __init__(self, cycle: int, bot) -> None:
def __init__(self, cycle: int, bot: commands.Bot) -> None:
"""
Generates all dates relevant to the election cycle from input election start, term start and end dates.
@@ -38,7 +41,7 @@ class ElectionCycle:
self.highcouncil_election_announce: datetime = self.voting_end + timedelta(days=1)
self.highcouncil_election_start: datetime = self.highcouncil_election_announce + timedelta(days=3)
self.highcouncil_election_end: datetime = self.term_start - timedelta(days=1)
self.bot: commands.Bot = bot
self.guild: nextcord.Guild = bot.get_guild(self.config.params.guild_id)
def build_election_announcement(self) -> list[str]:
"""
@@ -68,7 +71,7 @@ class ElectionCycle:
return message.split("---message_break---")
def build_highcouncil_election(self) -> str:
def build_highcouncil_election_announce(self) -> str:
"""
Builds message to announce highcouncil election start.
@@ -79,7 +82,13 @@ class ElectionCycle:
with open("bot_messages/highcouncil_election.txt", mode="r", encoding="utf8") as file:
message = file.read()
return message.format()
return message.format(
clan_council_elect_role_id=self.config.params.clan_council_elect_role_id,
highcouncil_election_announce=self.highcouncil_election_announce.strftime("%B %d"),
highcouncil_election_start=self.highcouncil_election_start.strftime("%B %d"),
highcouncil_election_end=self.highcouncil_election_end.strftime("%B %d"),
term_start_date=self.term_start.strftime("%B %d"),
)
def build_vote_start(self) -> str:
"""
@@ -105,7 +114,6 @@ class ElectionCycle:
Function checks current date to see if something needs to be run.
"""
today = datetime.today()
print("checking the date")
# check all dates if action is needed
if today.date() == self.election_start.date():
@@ -120,63 +128,81 @@ class ElectionCycle:
await self.term_start_action()
elif today.date() == self.term_end.date():
await self.term_end_action()
else:
print("today was not a special date")
print(today)
async def election_start_action(self) -> None:
"""
Performs required actions for when the election cycle starts.
"""
print("election cycle has started")
# create new channel for election
category: nextcord.CategoryChannel = self.bot.get_channel(self.config.params.active_category_id)
quarter_name: str = str(datetime.today().year) + "-" + self.elecion_cycle.name + "-election"
# TODO need to store this id in the sqlite database
category: nextcord.CategoryChannel = self.guild.get_channel(self.config.params.active_category_id)
quarter_name: str = self.get_election_cycle_name()
# create new entry into database.
election_channel = await category.create_text_channel(quarter_name)
with Session(engine) as session:
new_cycle = ElectionInformation(ElectionCycle=quarter_name, ElectionChannel=election_channel.id)
session.add(new_cycle)
session.commit()
# post message announcing election
# election start message is returned as an array of string because it is too long. send each string
# individually.
messages = self.build_election_announcement()
for message in messages:
await election_channel.send(message)
async def voting_start_action(self) -> None:
"""
Performs required actions for when voting for candidates starts.
Sends message informing members that voting has started.
Removes write permission from the general clan member role.
Adds the voting emote to all messages not sent by the vote. (This is temporary until voting is managed.)
"""
print("Voting has started")
await self.message_me("Voting has started")
message = self.build_vote_start()
clan_member_role: nextcord.Role = self.guild.get_role(self.config.params.clan_member_role_id)
with Session(engine) as session:
stmt = select(ElectionInformation).where(
ElectionInformation.ElectionCycle == self.get_election_cycle_name()
)
election_info = session.scalar(stmt)
election_channel = self.guild.get_channel(election_info.ElectionChannel)
await election_channel.send(message)
await election_channel.set_permissions(clan_member_role, send_messages=False)
async def voting_end_action(self) -> None:
"""
Performs required actions for when voting time has ended.
"""
print("voting has ended")
await self.message_me("voting has ended")
message = self.build_highcouncil_election_announce()
council_elect_role: nextcord.Role = self.guild.get_role(self.config.params.clan_council_elect_role_id)
with Session(engine) as session:
stmt = select(ElectionInformation).where(
ElectionInformation.ElectionCycle == self.get_election_cycle_name()
)
election_info = session.scalar(stmt)
election_channel = self.guild.get_channel(election_info.ElectionChannel)
await election_channel.send(message)
await election_channel.set_permissions(council_elect_role, send_messages=True)
async def highcouncil_election_action(self) -> None:
"""
Performs required actions for when the highcouncil is elected.
"""
print("Highcouncil election has started.")
await self.message_me("Highcouncil election has started.")
async def term_start_action(self) -> None:
msg: str = "the term has started"
print(msg)
self.message_me(msg)
async def term_end_action(self):
msg: str = "the term has ended"
print(msg)
self.message_me(msg)
async def message_me(self, msg: str):
def get_election_cycle_name(self) -> str:
"""
message a string to me
Builds the unique name of the current election cycle
Returns:
str: the name of the election cycle
"""
imcompiling: nextcord.User = await self.bot.fetch_user(206602713511886849)
await imcompiling.send(msg)
return f"{self.election_start.year} {self.elecion_cycle.name} election"
+11
View File
@@ -53,3 +53,14 @@ class TestCog(commands.Cog, name="Test commands."):
cycle = ElectionCycle(0, self.bot)
await cycle.election_start_action()
@test.subcommand()
async def voting_start(self, interaction: nextcord.Interaction):
"""
tests the voting start action
Args:
interaction (nextcord.Interaction): _description_
"""
cycle = ElectionCycle(0, self.bot)
await cycle.voting_start_action()
+1
View File
@@ -18,6 +18,7 @@ class Params:
clan_info_channel_id: int
clan_member_role_id: int
discord_token: str
guild_id: int
high_council_role_id: int
voting_emote_id: str
+2
View File
@@ -6,6 +6,7 @@ import nextcord
from hydra.core.config_store import ConfigStore
from nextcord.ext import commands
import database.create_database as create_database
from config import DakinyConfig
cs = ConfigStore.instance()
@@ -38,6 +39,7 @@ def main(cfg: DakinyConfig):
async def on_ready():
"""When Discord is connected"""
assert bot.user is not None
create_database # pylint: disable=pointless-statement
print(f"{bot.user.name} has connected to Discord!")
# Run Discord bot
+15
View File
@@ -0,0 +1,15 @@
"""
Small script to create the database if it doesn't already exist.
"""
import sqlite3
from os.path import exists
if not exists("dakiny.db"):
with sqlite3.connect("dakiny.db") as con:
cur = con.cursor()
with open("database/database.sql", mode="r", encoding="utf-8") as file:
database_script = file.read()
cur.executescript(database_script)
con.commit()
+27
View File
@@ -0,0 +1,27 @@
-- CREATE DATABASE dakiny;
-- use dakiny;
CREATE TABLE ElectionInformation(
ElectionCycle varchar(30) not null unique,
ElectionChannel bigint unsigned not null,
PRIMARY KEY (ElectionCycle)
);
CREATE TABLE CouncilMembers(
ElectionCycle varchar(30) not null unique,
MemberId bigint unsigned not null,
Elected boolean DEFAULT false,
HighCouncilElect boolean DEFAULT false,
DiscordManager boolean DEFAULT false,
ClanLeader boolean DEFAULT false,
FOREIGN KEY (ElectionCycle) REFERENCES ElectionInformation(ElectionCycle),
PRIMARY KEY (ElectionCycle, MemberId)
);
CREATE TABLE Votes(
ElectionCycle varchar(30) not null unique,
VoterId bigint unsigned not null,
VotedForId bigint unsigned not null,
FOREIGN KEY (ElectionCycle) REFERENCES ElectionInformation(ElectionCycle),
PRIMARY KEY (ElectionCycle, VoterId)
);
+16
View File
@@ -0,0 +1,16 @@
"""
Simple file to set up the sqlalchemy connection to database.
"""
from sqlalchemy import create_engine
from sqlalchemy.ext.automap import automap_base
# connect with sql alchemy and autoload schema
URL = "sqlite+pysqlite:///dakiny.db"
engine = create_engine(URL)
Base = automap_base()
Base.prepare(autoload_with=engine)
# define the classes
ElectionInformation = Base.classes.ElectionInformation
CouncilMembers = Base.classes.CouncilMembers
Votes = Base.classes.Votes
+1
View File
@@ -1,3 +1,4 @@
Discord permissions required for bot to operate:
manage channels
manage roles
+1
View File
@@ -2,3 +2,4 @@ nextcord==2.4.2
python-dotenv==1.0.0
pytz==2022.7.1
hydra-core==1.3.2
sqlalchemy<2.0