Compare commits

...
6 Commits
Author SHA1 Message Date
imcompiling 85a3ea0418 Merge branch '54-Only-edit-changed-channels'
build_latest / build (push) Successful in 17s
build_release / build (push) Successful in 16s
2024-05-16 22:44:08 -04:00
imcompiling 0b27c60bea Clarify some method names.
build_develop / build (push) Successful in 57s
current election cycle shoud return quarter cycle if it can't find a cycle.
2024-05-16 22:41:27 -04:00
imcompiling 1a6f373e03 Merge branch 'main' into 54-Only-edit-changed-channels
build_develop / build (push) Successful in 57s
2024-05-16 22:02:56 -04:00
imcompiling 00c8227b8e Update refresh messages command to be able to choose a channel.
build_latest / build (push) Failing after 17s
build_develop / build (push) Failing after 17s
2024-05-16 21:45:36 -04:00
imcompiling 04c44bc470 only trigger updating clan messages in updated directories.
Update clan info message if the template was updated.
2024-05-16 21:37:10 -04:00
imcompiling 3761d3bb77 Add BaseModel class for the gitea webhook packet. 2024-05-16 18:01:52 -04:00
9 changed files with 303 additions and 64 deletions
+9 -14
View File
@@ -28,7 +28,7 @@ from utils import (
get_cl_election_name,
get_datetime,
get_dm_election_name,
get_election_cycle_name,
get_election_name,
)
@@ -199,7 +199,7 @@ class ElectionCycle:
parent: nextcord.TextChannel = self.guild.get_channel(
self.config.params.voting_parent_id
)
quarter_name: str = get_election_cycle_name(self.config)
quarter_name: str = get_election_name(self.config)
thread_msg: nextcord.Message = await parent.send(content=quarter_name)
@@ -247,8 +247,7 @@ class ElectionCycle:
with Session(engine) as session:
stmt = select(ElectionInformation).where(
ElectionInformation.ElectionCycle
== get_election_cycle_name(self.config)
ElectionInformation.ElectionCycle == get_election_name(self.config)
)
info = session.scalar(stmt)
info.CouncilVoteMsgId = vote_msg.id
@@ -277,7 +276,7 @@ class ElectionCycle:
message, view=NominateHighCouncilView(self.config, self.guild)
)
election_name = get_election_cycle_name(self.config)
election_name = get_election_name(self.config)
with Session(engine) as session:
# remove vote button from vote message
@@ -333,8 +332,7 @@ class ElectionCycle:
with Session(engine) as session:
stmt = select(ElectionInformation).where(
ElectionInformation.ElectionCycle
== get_election_cycle_name(self.config)
ElectionInformation.ElectionCycle == get_election_name(self.config)
)
info = session.scalar(stmt)
info.HighCouncilVoteMsgId = vote_msg.id
@@ -364,9 +362,7 @@ class ElectionCycle:
# role management
stmt = (
select(CouncilMembers.MemberId)
.where(
CouncilMembers.ElectionCycle == get_election_cycle_name(self.config)
)
.where(CouncilMembers.ElectionCycle == get_election_name(self.config))
.where(CouncilMembers.Elected == true())
)
@@ -390,7 +386,7 @@ class ElectionCycle:
stmt = select(Votes.VotedForId).where(Votes.ElectionCycle == cl_election)
cl_votes = collections.Counter(session.scalars(stmt))
election_cycle = get_election_cycle_name(self.config)
election_cycle = get_election_name(self.config)
try:
dm_winner = max(dm_votes, key=dm_votes.get)
stmt = (
@@ -422,7 +418,7 @@ class ElectionCycle:
# clear view from vote message
stmt = select(ElectionInformation.HighCouncilVoteMsgId).where(
ElectionInformation.ElectionCycle == get_election_cycle_name(self.config)
ElectionInformation.ElectionCycle == get_election_name(self.config)
)
vote_msg_id = session.scalar(stmt)
vote_msg: nextcord.Message = await election_thread.fetch_message(vote_msg_id)
@@ -451,8 +447,7 @@ class ElectionCycle:
with Session(engine) as session:
stmt = select(ElectionInformation).where(
ElectionInformation.ElectionCycle
== get_election_cycle_name(self.config)
ElectionInformation.ElectionCycle == get_election_name(self.config)
)
return session.scalar(stmt)
+195
View File
@@ -0,0 +1,195 @@
# generated by datamodel-codegen:
# filename: gitea_webhook.json
# timestamp: 2024-05-16T21:15:27+00:00
from __future__ import annotations
from typing import List
from pydantic import BaseModel
class Author(BaseModel):
name: str
email: str
username: str
class Committer(BaseModel):
name: str
email: str
username: str
class Commit(BaseModel):
id: str
message: str
url: str
author: Author
committer: Committer
verification: None
timestamp: str
added: List[str]
removed: List[str]
modified: List[str]
class HeadCommit(BaseModel):
id: str
message: str
url: str
author: Author
committer: Committer
verification: None
timestamp: str
added: List[str]
removed: List[str]
modified: List[str]
class Owner(BaseModel):
id: int
login: str
login_name: str
full_name: str
email: str
avatar_url: str
language: str
is_admin: bool
last_login: str
created: str
restricted: bool
active: bool
prohibit_login: bool
location: str
website: str
description: str
visibility: str
followers_count: int
following_count: int
starred_repos_count: int
username: str
class Permissions(BaseModel):
admin: bool
push: bool
pull: bool
class Repository(BaseModel):
id: int
owner: Owner
name: str
full_name: str
description: str
empty: bool
private: bool
fork: bool
template: bool
parent: None
mirror: bool
size: int
language: str
languages_url: str
html_url: str
url: str
link: str
ssh_url: str
clone_url: str
original_url: str
website: str
stars_count: int
forks_count: int
watchers_count: int
open_issues_count: int
open_pr_counter: int
release_counter: int
default_branch: str
archived: bool
created_at: str
updated_at: str
archived_at: str
permissions: Permissions
has_issues: bool
has_wiki: bool
has_pull_requests: bool
has_projects: bool
has_releases: bool
has_packages: bool
has_actions: bool
ignore_whitespace_conflicts: bool
allow_merge_commits: bool
allow_rebase: bool
allow_rebase_explicit: bool
allow_squash_merge: bool
allow_rebase_update: bool
default_delete_branch_after_merge: bool
default_merge_style: str
default_allow_maintainer_edit: bool
avatar_url: str
internal: bool
mirror_interval: str
mirror_updated: str
repo_transfer: None
class Pusher(BaseModel):
id: int
login: str
login_name: str
full_name: str
email: str
avatar_url: str
language: str
is_admin: bool
last_login: str
created: str
restricted: bool
active: bool
prohibit_login: bool
location: str
website: str
description: str
visibility: str
followers_count: int
following_count: int
starred_repos_count: int
username: str
class Sender(BaseModel):
id: int
login: str
login_name: str
full_name: str
email: str
avatar_url: str
language: str
is_admin: bool
last_login: str
created: str
restricted: bool
active: bool
prohibit_login: bool
location: str
website: str
description: str
visibility: str
followers_count: int
following_count: int
starred_repos_count: int
username: str
class GiteaWebhook(BaseModel):
ref: str
before: str
after: str
compare_url: str
commits: List[Commit]
total_commits: int
head_commit: HeadCommit
repository: Repository
pusher: Pusher
sender: Sender
+5 -5
View File
@@ -13,7 +13,7 @@ from utils import (
NOMINEE_SPLITTER,
decorate_cl_nomination,
decorate_dm_nomination,
get_election_cycle_name,
get_election_name,
)
@@ -60,7 +60,7 @@ class NominateDMModal(nextcord.ui.Modal):
with Session(engine) as session:
# user already in table
cycle = get_election_cycle_name(self.config)
cycle = get_election_name(self.config)
stmt = (
select(CouncilMembers)
.where(CouncilMembers.ElectionCycle == cycle)
@@ -127,7 +127,7 @@ class NominateCLModal(nextcord.ui.Modal):
with Session(engine) as session:
# user already in table
cycle = get_election_cycle_name(self.config)
cycle = get_election_name(self.config)
stmt = (
select(CouncilMembers)
.where(CouncilMembers.ElectionCycle == cycle)
@@ -189,7 +189,7 @@ class NominateHighCouncilView(nextcord.ui.View):
)
return
cycle = get_election_cycle_name(self.config)
cycle = get_election_name(self.config)
usr_nominated = False
speech_content = ""
with Session(engine) as session:
@@ -242,7 +242,7 @@ class NominateHighCouncilView(nextcord.ui.View):
)
return
cycle = get_election_cycle_name(self.config)
cycle = get_election_name(self.config)
usr_nominated = False
speech_content = ""
with Session(engine) as session:
+4 -4
View File
@@ -9,7 +9,7 @@ from sqlalchemy.orm import Session
from config import DakinyConfig
from database_setup.sql_data_classes import CouncilMembers, engine
from utils import NOMINEE_SPLITTER, decorate_nomination, get_election_cycle_name
from utils import NOMINEE_SPLITTER, decorate_nomination, get_election_name
class NominateModal(nextcord.ui.Modal):
@@ -58,7 +58,7 @@ class NominateModal(nextcord.ui.Modal):
msg = await interaction.channel.send(speech)
council_member = CouncilMembers(
ElectionCycle=get_election_cycle_name(self.config),
ElectionCycle=get_election_name(self.config),
MemberId=interaction.user.id,
SpeechMsgId=msg.id,
)
@@ -66,7 +66,7 @@ class NominateModal(nextcord.ui.Modal):
session.commit()
else:
# user already nominated, just need to update the election speech
cycle = get_election_cycle_name(self.config)
cycle = get_election_name(self.config)
stmt = (
select(CouncilMembers)
.where(CouncilMembers.ElectionCycle == cycle)
@@ -120,7 +120,7 @@ class NominateView(nextcord.ui.View):
)
return
cycle = get_election_cycle_name(self.config)
cycle = get_election_name(self.config)
usr_nominated = False
speech_content = ""
with Session(engine) as session:
+14 -10
View File
@@ -21,7 +21,7 @@ from utils import (
build_highcouncil_votes_str,
get_cl_election_name,
get_dm_election_name,
get_election_cycle_name,
get_election_name,
)
@@ -49,7 +49,7 @@ class VoteSelect(nextcord.ui.StringSelect):
# should never happen
raise ValueError('election type was not "cl" or "dm"')
cycle: str = get_election_cycle_name(self.config)
cycle: str = get_election_name(self.config)
with Session(engine) as session:
stmt = select(ElectionInformation.HighCouncilVoteMsgId).where(
ElectionInformation.ElectionCycle == cycle
@@ -135,7 +135,7 @@ class VoteHighcouncilView(nextcord.ui.View):
)
return
cycle: str = get_election_cycle_name(self.config)
cycle: str = get_election_name(self.config)
election: str = get_cl_election_name(self.config)
with Session(engine) as session:
@@ -154,9 +154,11 @@ class VoteHighcouncilView(nextcord.ui.View):
options = [
SelectOption(
label=f"{self.guild.get_member(nominee).display_name} ☑️"
if voted_for == nominee
else f"{self.guild.get_member(nominee).display_name}",
label=(
f"{self.guild.get_member(nominee).display_name} ☑️"
if voted_for == nominee
else f"{self.guild.get_member(nominee).display_name}"
),
value=str(nominee),
)
for nominee in sorted(
@@ -211,7 +213,7 @@ class VoteHighcouncilView(nextcord.ui.View):
)
return
cycle: str = get_election_cycle_name(self.config)
cycle: str = get_election_name(self.config)
election: str = get_dm_election_name(self.config)
with Session(engine) as session:
@@ -230,9 +232,11 @@ class VoteHighcouncilView(nextcord.ui.View):
options = [
SelectOption(
label=f"{self.guild.get_member(nominee).display_name} ☑️"
if voted_for == nominee
else f"{self.guild.get_member(nominee).display_name}",
label=(
f"{self.guild.get_member(nominee).display_name} ☑️"
if voted_for == nominee
else f"{self.guild.get_member(nominee).display_name}"
),
value=str(nominee),
)
for nominee in sorted(
+8 -6
View File
@@ -17,7 +17,7 @@ from database_setup.sql_data_classes import (
Votes,
engine,
)
from utils import build_votes_str, get_election_cycle_name
from utils import build_votes_str, get_election_name
class VoteSelect(nextcord.ui.StringSelect):
@@ -31,7 +31,7 @@ class VoteSelect(nextcord.ui.StringSelect):
self.config = config
async def callback(self, interaction: Interaction):
election_name = get_election_cycle_name(self.config)
election_name = get_election_name(self.config)
with Session(engine) as session:
stmt = select(ElectionInformation.CouncilVoteMsgId).where(
ElectionInformation.ElectionCycle == election_name
@@ -114,7 +114,7 @@ class VoteView(nextcord.ui.View):
)
return
election: str = get_election_cycle_name(self.config)
election: str = get_election_name(self.config)
with Session(engine) as session:
stmt = (
@@ -130,9 +130,11 @@ class VoteView(nextcord.ui.View):
options = [
SelectOption(
label=f"{self.guild.get_member(nominee).display_name} ☑️"
if voted_for == nominee
else f"{self.guild.get_member(nominee).display_name}",
label=(
f"{self.guild.get_member(nominee).display_name} ☑️"
if voted_for == nominee
else f"{self.guild.get_member(nominee).display_name}"
),
value=str(nominee),
)
for nominee in sorted(
+26 -3
View File
@@ -10,8 +10,9 @@ from fastapi import FastAPI
from git import Repo
from nextcord.ext import commands
from classes.gitea_webhook import GiteaWebhook
from config import DakinyConfig
from utils import REPO_DIR, update_clan_messages
from utils import REPO_DIR, edit_clan_council_info, update_clan_messages
app = FastAPI()
REPO_URL = "https://gitea.cheezers.org/imcompiling/dakiny-messages.git"
@@ -38,16 +39,38 @@ class ClanMessages(commands.Cog, name="Clan Messages"):
server = uvicorn.Server(config)
await server.serve()
async def update_messages(self):
async def update_messages(self, hook_info: GiteaWebhook):
"""
Pulls from git and then calls method to update all the discord messages as described by the
files in the git repo.
"""
# Get all modified/added files
modified_files: list[str] = [
file
for list in [commit.modified + commit.added for commit in hook_info.commits]
for file in list
]
template_updates = {
file.split("/")[1] for file in modified_files if "message_templates" in file
}
channel_updates = {
file.split("/")[1]
for file in modified_files
if file.split("/")[1] not in template_updates
}
self.origin.pull()
guild: nextcord.Guild = self.bot.get_guild(self.config.params.guild_id)
await update_clan_messages(guild)
await update_clan_messages(guild, channel_updates)
# if the council members template has changed, update the message
if "council_members.txt" in template_updates:
await edit_clan_council_info(
self.bot.get_guild(self.config.params.guild_id), self.config
)
return {"message": "Successfully updated all discord messages."}
+23 -3
View File
@@ -2,10 +2,12 @@
Contained commands and views for refresh commands.
"""
import os
import nextcord
from nextcord.ext import commands
from utils import edit_clan_council_info, update_clan_messages
from utils import MESSAGES_DIR, edit_clan_council_info, update_clan_messages
class RefreshCog(commands.Cog, name="Refresh Commands"):
@@ -29,15 +31,33 @@ class RefreshCog(commands.Cog, name="Refresh Commands"):
"""
@refresh.subcommand(description="Force refresh all of the bot managed messages.")
async def messages(self, interaction: nextcord.Interaction):
async def messages(
self,
interaction: nextcord.Interaction,
channel: nextcord.TextChannel = nextcord.SlashOption(
description="Channel to refresh messages in.",
required=False,
),
):
"""
Calls update_clan_messages() to update all messages.
Args:
interaction (nextcord.Interaction): Interaction with the command.
channel (nextcord.TextChannel): channel to refresh messages in.
"""
await interaction.response.defer(ephemeral=True)
await update_clan_messages(interaction.guild)
if channel is None:
channel_updates = [
dir_name
for dir_name in os.listdir(MESSAGES_DIR)
if os.path.isdir(f"{MESSAGES_DIR}/{dir_name}")
]
else:
channel_updates = [channel.name]
await update_clan_messages(interaction.guild, channel_updates)
await interaction.followup.send("Messages have been refreshed.", ephemeral=True)
@refresh.subcommand(description="Updates the current council info message.")
+19 -19
View File
@@ -4,6 +4,7 @@ File contains util functions.
import os
from datetime import datetime
from typing import Iterable
import nextcord
from sqlalchemy import select, true, update
@@ -48,7 +49,7 @@ def get_datetime(day: str) -> datetime:
return datetime(year, month, day)
def get_election_cycle_name(config: DakinyConfig) -> str:
def get_election_name(config: DakinyConfig) -> str:
"""
Builds the unique name of the current election cycle
@@ -64,7 +65,7 @@ def get_election_cycle_name(config: DakinyConfig) -> str:
return f"vote-{get_datetime(current_cycle.term_start).year}-{current_cycle.name}"
def get_cycle_name(config: DakinyConfig) -> str:
def get_quarter_name(config: DakinyConfig) -> str:
"""
Builds the name of the current cycle/quarter
@@ -75,7 +76,7 @@ def get_cycle_name(config: DakinyConfig) -> str:
str: the name of the cycle.
"""
cycle = get_current_cycle(config)
cycle = get_quarter_cycle(config)
current_cycle = config.election_cycles[cycle]
return f"{get_datetime(current_cycle.term_start).year}-{current_cycle.name}"
@@ -92,7 +93,7 @@ def get_dm_election_name(config: DakinyConfig) -> str:
str: the name of the election
"""
return f"{get_election_cycle_name(config)}-dm"
return f"{get_election_name(config)}-dm"
def get_cl_election_name(config: DakinyConfig) -> str:
@@ -106,7 +107,7 @@ def get_cl_election_name(config: DakinyConfig) -> str:
str: the name of the election
"""
return f"{get_election_cycle_name(config)}-cl"
return f"{get_election_name(config)}-cl"
def get_current_election_cycle(config: DakinyConfig) -> int:
@@ -129,10 +130,11 @@ def get_current_election_cycle(config: DakinyConfig) -> int:
):
return cycle
return None
# It isn't a current election, get the quarter
return get_quarter_cycle(config)
def get_current_cycle(config: DakinyConfig) -> int:
def get_quarter_cycle(config: DakinyConfig) -> int:
"""
Returns the index for the current cycle/quarter. Defined as between term start and end. Shouldn't return none
@@ -170,7 +172,7 @@ def build_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> str:
with Session(engine) as session:
stmt = select(CouncilMembers).where(
CouncilMembers.ElectionCycle == get_election_cycle_name(config)
CouncilMembers.ElectionCycle == get_election_name(config)
)
for nominee in sorted(
@@ -179,9 +181,7 @@ def build_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> str:
):
votes[nominee.MemberId] = 0
stmt = select(Votes).where(
Votes.ElectionCycle == get_election_cycle_name(config)
)
stmt = select(Votes).where(Votes.ElectionCycle == get_election_name(config))
for vote in session.scalars(stmt):
if vote.VotedForId is not None:
@@ -211,7 +211,7 @@ def build_highcouncil_votes_str(config: DakinyConfig, guild: nextcord.Guild) ->
with Session(engine) as session:
stmt = (
select(CouncilMembers)
.where(CouncilMembers.ElectionCycle == get_election_cycle_name(config))
.where(CouncilMembers.ElectionCycle == get_election_name(config))
.filter(CouncilMembers.ClanLeaderSpeechId.is_not(None))
)
@@ -229,7 +229,7 @@ def build_highcouncil_votes_str(config: DakinyConfig, guild: nextcord.Guild) ->
stmt = (
select(CouncilMembers)
.where(CouncilMembers.ElectionCycle == get_election_cycle_name(config))
.where(CouncilMembers.ElectionCycle == get_election_name(config))
.filter(CouncilMembers.DiscordManagerSpeechId.is_not(None))
)
@@ -337,7 +337,7 @@ def record_event_participation(config: DakinyConfig, users: list[str], is_member
is_member (bool): whether the users are a discord member or not
"""
cycle = get_cycle_name(config)
cycle = get_quarter_name(config)
participation_point_value = config.params.event_participate_points
with Session(engine) as session:
for user in users:
@@ -371,7 +371,7 @@ def record_event_host(config: DakinyConfig, user: int):
config (DakinyConfig): Dakiny config for getting points
user (int): Id of user that hosted.
"""
cycle = get_cycle_name(config)
cycle = get_quarter_name(config)
host_point_value = config.params.event_host_points
with Session(engine) as session:
stmt = (
@@ -395,7 +395,7 @@ def record_event_host(config: DakinyConfig, user: int):
session.commit()
async def update_clan_messages(guild: nextcord.Guild):
async def update_clan_messages(guild: nextcord.Guild, channel_updates: Iterable[str]):
"""
Updates all clan messages to contents in MESSAGES_DIR.
Does not to any git work. Is expected to be called after git is pulled.
@@ -411,7 +411,7 @@ async def update_clan_messages(guild: nextcord.Guild):
dirs = [
dir_name
for dir_name in os.listdir(MESSAGES_DIR)
for dir_name in channel_updates
if os.path.isdir(f"{MESSAGES_DIR}/{dir_name}")
]
@@ -544,7 +544,7 @@ async def edit_clan_council_info(guild: nextcord.Guild, config: DakinyConfig):
config.params.clan_info_channel_id
)
election = get_election_cycle_name(config)
election = get_election_name(config)
with Session(engine) as session:
stmt = (
select(CouncilMembers.MemberId)
@@ -570,7 +570,7 @@ async def edit_clan_council_info(guild: nextcord.Guild, config: DakinyConfig):
if id not in (discord_manager, clan_leader)
]
quarter = get_cycle_name(config)
quarter = get_quarter_name(config)
with open(
f"{TEMPLATES_DIR}/council_members.txt", mode="r", encoding="utf8"