Compare commits

..
Author SHA1 Message Date
imcompiling 0ff13453be Fix docker file.
build_latest / build (push) Successful in 59s
build_release / build (push) Successful in 25s
2024-05-16 22:02:31 -04:00
imcompiling 6b29e26140 Revert "Add a fail notification to builds."
This reverts commit bbcb4e4dff.
2024-05-16 22:02:22 -04:00
imcompiling bbcb4e4dff Add a fail notification to builds.
build_latest / build (push) Failing after 25s
2024-05-16 21:59:34 -04:00
imcompiling a76bc02d4a Merge branch '55-Use-dakiny-messages-repo-for-message-templates'
build_release / build (push) Failing after 20s
build_latest / build (push) Failing after 16s
2024-05-16 16:47:19 -04:00
imcompiling 3890c30d1f Found niche bug when testing. Need to get election cycle here because election cycle might be different than current cycle.
build_develop / build (push) Failing after 19s
2024-05-16 16:41:36 -04:00
imcompiling 1e1407c918 Update templates to be used from dakiny messages repo. 2024-05-16 16:25:50 -04:00
imcompiling be408307aa Remove template messages from repo. 2024-05-16 16:22:18 -04:00
imcompiling 069dafe9ae Merge branch '56-announce-clan-council'
build_latest / build (push) Successful in 21s
build_release / build (push) Successful in 16s
build_develop / build (push) Successful in 17s
2024-05-16 16:10:30 -04:00
imcompiling 7d54a0c30b Create refresh council info command. Change method name.
build_develop / build (push) Successful in 59s
2024-05-16 16:10:16 -04:00
imcompiling 39fd55146e move edit clan council message into utils. 2024-05-16 16:04:08 -04:00
imcompiling a8323c9a88 Update the message to look more like current. 2024-05-16 15:55:09 -04:00
imcompiling 51d6713940 format returns the string. 2024-05-16 15:45:33 -04:00
imcompiling 65ed5e7b59 Made function to gather and send the council members message in the clan info channel. 2024-05-16 15:37:34 -04:00
imcompiling 2059814394 Broke edit clan message to own function to allow for usage in other places. 2024-05-16 15:36:50 -04:00
imcompiling 063baa07ee Merge branch '53-Not-recording-High-Council-results-in-CouncilMembers-database'
build_latest / build (push) Successful in 55s
build_develop / build (push) Successful in 16s
2024-05-16 14:53:55 -04:00
imcompiling 370bd71154 The defer needs to be ephemeral too.
build_latest / build (push) Successful in 57s
build_release / build (push) Successful in 22s
2024-05-16 09:55:33 -04:00
10 changed files with 134 additions and 122 deletions
-1
View File
@@ -13,7 +13,6 @@ COPY config.py config.py
COPY main.py main.py COPY main.py main.py
COPY utils.py utils.py COPY utils.py utils.py
COPY bot_messages bot_messages/
COPY classes classes/ COPY classes classes/
COPY cogs cogs/ COPY cogs cogs/
COPY database_setup database_setup/ COPY database_setup database_setup/
-7
View File
@@ -1,7 +0,0 @@
Here's the {quarter} Clan Council:
Dakiny Council:
{council}
Dakiny High Council:
{high_council}
-54
View File
@@ -1,54 +0,0 @@
```fix
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.
This clan is about human interaction, and the Clan Council positions serve to prop up certain voices within our community, in an effort to shape it as a force for good. Our mission here is to spread love. The more loving Council Members (CMs) we have on the team, the greater the community will grow for all of us to enjoy. There are no term limits, but if you go inactive on RS or become uninterested in pouring your effort into this community, request to step down so that others may contribute where you are no longer able to.
We want all CMs to grow and succeed. Please, reach out for assistance and advice if you find yourself going even a few days without taking real, actionable steps towards the prosperity of Dakiny. Recruiting, hosting events, participating in the Clan Council Discord, and demonstrating the Core Elements of the position are required from each and every CM to grow a happy and healthy clan.
__So here's how it works:__
---message_break---
Each quarter, a channel is created here on Discord for clan members to cast their vote to determine the Clan Council for that 3-month period.
**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 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.
Then, the new Clan Council Elect will come together to select the 2 High Council positions:
RuneScape 3 Clan Leader
Discord Server Manager
The Clan Council will be denoted by the Discord ranks of <@&{clan_council_id}> and <@&{high_council_id}> and in-game with the following ranks.
<:Rank_Admin:479008185240518656> Admin
<:Rank_Organiser:479008215024009216> Organizer
<:Rank_Coordinator:479008238654849045> Coordinator
<:Rank_Overseer:479008269382451201> Overseer
<:Rank_Deputy:479008295319896064> Deputy Owner
<:Rank_Owner:479008325137072155> Clan Leader
---message_break---
__Here's the timeline:__
Election begins: {election_start}
Term: {term_start} - {term_end}
**{election_start}**: Post here to announce candidacy
**{voting_start}**: Voting begins
**{voting_end}**: Votes are tallied and winners are announced
**{highcouncil_election}**: The new Clan Council votes who among them shall receive the two High Council positions of: Clan Leader and Discord Server Manager.
**{term_start}**: The new Clan Council is officially inaugurated and a drop party (or a giveaway event) begins to commemorate the new leadership.
This process will begin again next quarter on {next_election_start} with the inauguration scheduled for {term_end}.
---message_break---
**You may now announce your candidacy for a seat on the Dakiny Clan Council!
**
You may include a speech with your announcement, as well as specific pertinent facts such as total recruits, time in the clan, past Clan Council experience, and donations made. This data is available upon request via <@&{clan_council_id}>.
-12
View File
@@ -1,12 +0,0 @@
`Leadership Elections - candidacy time`
<@&{clan_council_elect_role_id}>, welcome. We are starting the second part of our elections. On {highcouncil_election_announce} this channel will be open to write for all of you. Everyone else will be able to read this channel, but not write in it. Only members of the Clan Council Elected (CCE) can vote now. Any votes from others will be annulled.
From {highcouncil_election_announce} to {highcouncil_election_start}, you will be able to announce your candidacy for High Council. The positions are:
Clan Leader
Discord Server Manager
Each member of the CCE can vote once in each category (Discord Server Manager and Clan Leader). Alternatively, you may choose to intentionally abstain.
Votes can be cast until {highcouncil_election_end}, so we can announce and have everyone set up to start the term by {term_start}.
-1
View File
@@ -1 +0,0 @@
<@&{clan_council_elect_role_id}> it is now time to vote for the high council! You have until {term_start} to complete the vote.
-14
View File
@@ -1,14 +0,0 @@
====================
<@&{clan_member_id}>, it's time to vote for clan council.
Press the Vote button to vote!
Voting ends next {voting_end_day}, {voting_end}.
Results will then be announced.
After a candidate receives 3 votes, their spot is secured in the new council, but you can still vote for them to show your support.
**Just a reminder:** only the people listed below are eligible to vote, and each clan member may **only vote for one person**. Votes from guests, diplomats, or allies will be annulled.
Votes for multiple candidates from the same person will not be counted.
RS3 Clan Members
OSRS Clan Members
+7 -4
View File
@@ -21,8 +21,10 @@ from database_setup.sql_data_classes import (
engine, engine,
) )
from utils import ( from utils import (
TEMPLATES_DIR,
build_highcouncil_votes_str, build_highcouncil_votes_str,
build_votes_str, build_votes_str,
edit_clan_council_info,
get_cl_election_name, get_cl_election_name,
get_datetime, get_datetime,
get_dm_election_name, get_dm_election_name,
@@ -76,7 +78,7 @@ class ElectionCycle:
""" """
with open( with open(
"bot_messages/election_announcement.txt", mode="r", encoding="utf8" f"{TEMPLATES_DIR}/election_announcement.txt", mode="r", encoding="utf8"
) as file: ) as file:
message = file.read() message = file.read()
@@ -109,7 +111,7 @@ class ElectionCycle:
""" """
with open( with open(
"bot_messages/highcouncil_election.txt", mode="r", encoding="utf8" f"{TEMPLATES_DIR}/highcouncil_election.txt", mode="r", encoding="utf8"
) as file: ) as file:
message = file.read() message = file.read()
@@ -136,7 +138,7 @@ class ElectionCycle:
""" """
with open( with open(
"bot_messages/highcouncil_vote.txt", mode="r", encoding="utf8" f"{TEMPLATES_DIR}/highcouncil_vote.txt", mode="r", encoding="utf8"
) as file: ) as file:
message = file.read() message = file.read()
@@ -153,7 +155,7 @@ class ElectionCycle:
str: Message to send str: Message to send
""" """
with open("bot_messages/vote_start.txt", mode="r", encoding="utf8") as file: with open(f"{TEMPLATES_DIR}/vote_start.txt", mode="r", encoding="utf8") as file:
message = file.read() message = file.read()
vote_end: datetime = get_datetime( vote_end: datetime = get_datetime(
self.config.election_cycles[self.cycle].voting_end self.config.election_cycles[self.cycle].voting_end
@@ -437,6 +439,7 @@ class ElectionCycle:
) )
await election_thread.edit(archived=True, locked=True) await election_thread.edit(archived=True, locked=True)
await edit_clan_council_info(self.guild, self.config)
def get_election_info(self) -> ElectionInformation: def get_election_info(self) -> ElectionInformation:
""" """
+1 -1
View File
@@ -21,7 +21,7 @@ def setup(bot: commands.Bot):
""" """
bot.add_cog(RecordCog(bot)) bot.add_cog(RecordCog(bot))
bot.add_cog(RefreshCog()) bot.add_cog(RefreshCog(bot))
bot.add_cog(RemoveCog()) bot.add_cog(RemoveCog())
bot.add_cog(RequestCog(bot)) bot.add_cog(RequestCog(bot))
bot.add_cog(TestCog(bot)) bot.add_cog(TestCog(bot))
+22 -2
View File
@@ -5,7 +5,7 @@ Contained commands and views for refresh commands.
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from utils import update_clan_messages from utils import edit_clan_council_info, update_clan_messages
class RefreshCog(commands.Cog, name="Refresh Commands"): class RefreshCog(commands.Cog, name="Refresh Commands"):
@@ -13,6 +13,13 @@ class RefreshCog(commands.Cog, name="Refresh Commands"):
Class holds refresh commands. Class holds refresh commands.
""" """
def __init__(self, bot) -> None:
"""
Default constructor for RefreshCog class. Does nothing.
"""
super().__init__()
self.bot: commands.Bot = bot
@nextcord.slash_command( @nextcord.slash_command(
default_member_permissions=nextcord.Permissions(administrator=True) default_member_permissions=nextcord.Permissions(administrator=True)
) )
@@ -29,6 +36,19 @@ class RefreshCog(commands.Cog, name="Refresh Commands"):
Args: Args:
interaction (nextcord.Interaction): Interaction with the command. interaction (nextcord.Interaction): Interaction with the command.
""" """
await interaction.response.defer() await interaction.response.defer(ephemeral=True)
await update_clan_messages(interaction.guild) await update_clan_messages(interaction.guild)
await interaction.followup.send("Messages have been refreshed.", ephemeral=True) await interaction.followup.send("Messages have been refreshed.", ephemeral=True)
@refresh.subcommand(description="Updates the current council info message.")
async def council_info(self, interaction: nextcord.Interaction):
"""
Calls edit_clan_council_info().
Args:
interaction (nextcord.Interaction): Interaction with the command.
"""
await edit_clan_council_info(interaction.guild, self.bot.config)
await interaction.response.send_message(
"Clan Council Info message updated.", ephemeral=True
)
+104 -26
View File
@@ -6,7 +6,7 @@ import os
from datetime import datetime from datetime import datetime
import nextcord import nextcord
from sqlalchemy import select, update from sqlalchemy import select, true, update
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from config import DakinyConfig from config import DakinyConfig
@@ -21,6 +21,7 @@ from database_setup.sql_data_classes import (
NOMINEE_SPLITTER = "========================================\n\n" NOMINEE_SPLITTER = "========================================\n\n"
REPO_DIR = "./dakiny-messages" REPO_DIR = "./dakiny-messages"
MESSAGES_DIR = f"{REPO_DIR}/messages" MESSAGES_DIR = f"{REPO_DIR}/messages"
TEMPLATES_DIR = f"{REPO_DIR}/message_templates"
def get_datetime(day: str) -> datetime: def get_datetime(day: str) -> datetime:
@@ -58,7 +59,9 @@ def get_election_cycle_name(config: DakinyConfig) -> str:
str: the name of the election cycle str: the name of the election cycle
""" """
return f"vote-{get_cycle_name(config)}" cycle = get_current_election_cycle(config)
current_cycle = config.election_cycles[cycle]
return f"vote-{get_datetime(current_cycle.term_start).year}-{current_cycle.name}"
def get_cycle_name(config: DakinyConfig) -> str: def get_cycle_name(config: DakinyConfig) -> str:
@@ -397,11 +400,10 @@ async def update_clan_messages(guild: nextcord.Guild):
Updates all clan messages to contents in MESSAGES_DIR. Updates all clan messages to contents in MESSAGES_DIR.
Does not to any git work. Is expected to be called after git is pulled. Does not to any git work. Is expected to be called after git is pulled.
for ever directory, sort the files by number then post for ever directory, sort the files by number then edits/sends the message.
posting involves checking if there is a message for that particular file then editing it or creating a
new one. the message could be deleted, which would cause a new message to be sent and database updated.
Channel fetching/management is handled by another function. Channel fetching/management is handled by another function.
Message editing and sending is handled by another function.
Args: Args:
guild (nextcord.Guild): Guild object that all channels and messages belong to. guild (nextcord.Guild): Guild object that all channels and messages belong to.
@@ -426,27 +428,9 @@ async def update_clan_messages(guild: nextcord.Guild):
message_name = f"{dir_name}/{file_name}" message_name = f"{dir_name}/{file_name}"
file_path = f"{dir_path}/{file_name}" file_path = f"{dir_path}/{file_name}"
with open(file_path, "r", encoding="utf8") as reader: with open(file_path, "r", encoding="utf8") as reader:
stmt = select(Messages.Id).where(Messages.Path == message_name) await edit_clan_message(
msg_id = session.scalar(stmt) session, channel, message_name, reader.read(2000)
if msg_id is None: )
# don't have a message for this file
msg = await channel.send(reader.read(2000))
new_message = Messages(Path=message_name, Id=msg.id)
session.add(new_message)
session.commit()
else:
try:
msg: nextcord.Message = await channel.fetch_message(msg_id)
await msg.edit(reader.read(2000))
except nextcord.NotFound:
msg = await channel.send(reader.read(2000))
stmt = (
update(Messages)
.where(Messages.Path == message_name)
.values(Id=msg.id)
)
session.execute(stmt)
session.commit()
async def get_clan_message_channel( async def get_clan_message_channel(
@@ -508,3 +492,97 @@ async def get_clan_message_channel(
session.execute(stmt) session.execute(stmt)
session.commit() session.commit()
return ret_channel return ret_channel
async def edit_clan_message(
session: Session,
channel: nextcord.TextChannel,
message_name: str,
message_content: str,
):
"""
Obtains a message id from database. If no id, then need to send a new message and record the id.
If there is a message, then edit the message. If that message is not found in discord, then need to
send a new message and record the id in the database.
Args:
session (Session): Open connection to database.
channel (nextcord.TextChannel): Channel object that message belongs to.
message_name (str): Name of the message. Corresponds to the file that contains message content.
message_content (str): Content of the message to edit/send.
"""
stmt = select(Messages.Id).where(Messages.Path == message_name)
msg_id = session.scalar(stmt)
if msg_id is None:
# don't have a message for this file
msg = await channel.send(message_content)
new_message = Messages(Path=message_name, Id=msg.id)
session.add(new_message)
session.commit()
else:
try:
msg: nextcord.Message = await channel.fetch_message(msg_id)
await msg.edit(message_content)
except nextcord.NotFound:
msg = await channel.send(message_content)
stmt = (
update(Messages).where(Messages.Path == message_name).values(Id=msg.id)
)
session.execute(stmt)
session.commit()
async def edit_clan_council_info(guild: nextcord.Guild, config: DakinyConfig):
"""
Gets the winners of the elections and updates the perminant message that states the current council.
Args:
guild (nextcord.Guild): Guild object that to use to get channel.
config (DakinyConfig): Configs
"""
clan_info_channel: nextcord.TextChannel = await guild.fetch_channel(
config.params.clan_info_channel_id
)
election = get_election_cycle_name(config)
with Session(engine) as session:
stmt = (
select(CouncilMembers.MemberId)
.where(CouncilMembers.ElectionCycle == election)
.where(CouncilMembers.DiscordManager == true())
)
discord_manager = session.scalar(stmt)
stmt = (
select(CouncilMembers.MemberId)
.where(CouncilMembers.ElectionCycle == election)
.where(CouncilMembers.ClanLeader == true())
)
clan_leader = session.scalar(stmt)
stmt = (
select(CouncilMembers.MemberId)
.where(CouncilMembers.ElectionCycle == election)
.where(CouncilMembers.Elected == true())
)
council_members_strs = [
f"<@{id}>"
for id in session.scalars(stmt)
if id not in (discord_manager, clan_leader)
]
quarter = get_cycle_name(config)
with open(
f"{TEMPLATES_DIR}/council_members.txt", mode="r", encoding="utf8"
) as file:
message = file.read()
message = message.format(
quarter=quarter,
council="\n".join(council_members_strs),
clan_leader=clan_leader,
discord_manager=discord_manager,
)
await edit_clan_message(
session, clan_info_channel, "council_members.txt", message
)