mirror of
https://github.com/hencaric/SCNewsBot.git
synced 2026-08-17 06:56:03 +01:00
249 lines
6.1 KiB
Python
249 lines
6.1 KiB
Python
import discord
|
|
from discord.ext import commands
|
|
from datetime import datetime, timedelta
|
|
import json
|
|
import os
|
|
|
|
# CONFIG
|
|
LEADERBOARD_FILE = "leaderboard.json"
|
|
EMBED_COLOR = discord.Color.gold()
|
|
TOP_EMOJIS = ["🥇", "🥈", "🥉"]
|
|
USERS_PER_PAGE = 5
|
|
|
|
# DATA
|
|
class LeaderboardData:
|
|
|
|
def __init__(self, filepath=LEADERBOARD_FILE):
|
|
self.filepath = filepath
|
|
self.data = {}
|
|
self.load()
|
|
|
|
def load(self):
|
|
if os.path.exists(self.filepath):
|
|
try:
|
|
with open(self.filepath, "r", encoding="utf-8") as f:
|
|
self.data = json.load(f)
|
|
except Exception:
|
|
self.data = {}
|
|
else:
|
|
self.data = {}
|
|
|
|
def save(self):
|
|
with open(self.filepath, "w", encoding="utf-8") as f:
|
|
json.dump(self.data, f, indent=4)
|
|
|
|
# RECORD POST
|
|
def record_post(self, user_id: int):
|
|
|
|
uid = str(user_id)
|
|
now = datetime.utcnow().isoformat()
|
|
|
|
if uid not in self.data:
|
|
self.data[uid] = {
|
|
"count": 0,
|
|
"last_post": now,
|
|
"posts": []
|
|
}
|
|
|
|
self.data[uid]["count"] += 1
|
|
self.data[uid]["last_post"] = now
|
|
self.data[uid]["posts"].append(now)
|
|
|
|
self.save()
|
|
|
|
# SORT
|
|
def get_sorted(self):
|
|
|
|
return sorted(
|
|
self.data.items(),
|
|
key=lambda item: (-item[1]["count"], item[1]["last_post"])
|
|
)
|
|
|
|
# USER STATS
|
|
def user_30_days(self, posts):
|
|
|
|
cutoff = datetime.utcnow() - timedelta(days=30)
|
|
|
|
return sum(
|
|
1 for p in posts
|
|
if datetime.fromisoformat(p) >= cutoff
|
|
)
|
|
|
|
def user_year(self, posts):
|
|
|
|
year = datetime.utcnow().year
|
|
|
|
return sum(
|
|
1 for p in posts
|
|
if datetime.fromisoformat(p).year == year
|
|
)
|
|
|
|
# GLOBAL STATS
|
|
def global_stats(self):
|
|
|
|
cutoff = datetime.utcnow() - timedelta(days=30)
|
|
year = datetime.utcnow().year
|
|
|
|
total30 = 0
|
|
totalYear = 0
|
|
totalAll = 0
|
|
|
|
for user in self.data.values():
|
|
|
|
posts = user.get("posts", [])
|
|
|
|
totalAll += user.get("count", 0)
|
|
|
|
for p in posts:
|
|
|
|
dt = datetime.fromisoformat(p)
|
|
|
|
if dt >= cutoff:
|
|
total30 += 1
|
|
|
|
if dt.year == year:
|
|
totalYear += 1
|
|
|
|
return total30, totalYear, totalAll
|
|
|
|
|
|
leaderboard_data = LeaderboardData()
|
|
|
|
# VIEW
|
|
class LeaderboardView(discord.ui.View):
|
|
|
|
def __init__(self, ctx, users):
|
|
|
|
super().__init__(timeout=180)
|
|
|
|
self.ctx = ctx
|
|
self.users = users
|
|
self.page = 0
|
|
self.max_page = (len(users) - 1) // USERS_PER_PAGE
|
|
|
|
def build_embed(self):
|
|
|
|
embed = discord.Embed(
|
|
title="📊 Announcement Leaderboard",
|
|
color=EMBED_COLOR,
|
|
timestamp=datetime.utcnow()
|
|
)
|
|
|
|
start = self.page * USERS_PER_PAGE
|
|
end = start + USERS_PER_PAGE
|
|
|
|
page_users = self.users[start:end]
|
|
|
|
for i, (uid, stats) in enumerate(page_users, start=start+1):
|
|
|
|
member = self.ctx.guild.get_member(int(uid))
|
|
name = member.display_name if member else f"User {uid}"
|
|
|
|
posts = stats.get("posts", [])
|
|
|
|
alltime = stats["count"]
|
|
last30 = leaderboard_data.user_30_days(posts)
|
|
year = leaderboard_data.user_year(posts)
|
|
|
|
last_post = datetime.fromisoformat(stats["last_post"]).strftime("%Y-%m-%d %H:%M UTC")
|
|
|
|
if i <= 3:
|
|
title = f"{TOP_EMOJIS[i-1]} {name}"
|
|
else:
|
|
title = name
|
|
|
|
value = (
|
|
f"**All Time:** {alltime}\n"
|
|
f"**30 Days:** {last30}\n"
|
|
f"**Year:** {year}\n"
|
|
f"**Last:** {last_post}"
|
|
)
|
|
|
|
if int(uid) == self.ctx.author.id:
|
|
value += " 👈 You"
|
|
|
|
embed.add_field(name=title, value=value, inline=False)
|
|
|
|
# GLOBAL STATS
|
|
total30, totalYear, totalAll = leaderboard_data.global_stats()
|
|
|
|
embed.add_field(
|
|
name="📈 Server Announcement Stats",
|
|
value=(
|
|
f"**Last 30 Days:** {total30}\n"
|
|
f"**This Year:** {totalYear}\n"
|
|
f"**All Time:** {totalAll}"
|
|
),
|
|
inline=False
|
|
)
|
|
|
|
embed.set_footer(text=f"Page {self.page+1}/{self.max_page+1}")
|
|
|
|
return embed
|
|
|
|
# BUTTONS
|
|
@discord.ui.button(label="⬅ Previous", style=discord.ButtonStyle.secondary)
|
|
async def previous(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
|
|
if self.page > 0:
|
|
self.page -= 1
|
|
|
|
await interaction.response.edit_message(embed=self.build_embed(), view=self)
|
|
|
|
@discord.ui.button(label="🧑 My Rank", style=discord.ButtonStyle.primary)
|
|
async def my_rank(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
|
|
uid = str(interaction.user.id)
|
|
|
|
for index, (user_id, _) in enumerate(self.users):
|
|
|
|
if user_id == uid:
|
|
|
|
self.page = index // USERS_PER_PAGE
|
|
break
|
|
|
|
await interaction.response.edit_message(embed=self.build_embed(), view=self)
|
|
|
|
@discord.ui.button(label="Next ➡", style=discord.ButtonStyle.secondary)
|
|
async def next(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
|
|
if self.page < self.max_page:
|
|
self.page += 1
|
|
|
|
await interaction.response.edit_message(embed=self.build_embed(), view=self)
|
|
|
|
|
|
# COG
|
|
class Leaderboard(commands.Cog):
|
|
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
@commands.command()
|
|
async def leaderboard(self, ctx):
|
|
|
|
users = leaderboard_data.get_sorted()
|
|
|
|
if not users:
|
|
await ctx.send("No announcements have been posted yet.")
|
|
return
|
|
|
|
view = LeaderboardView(ctx, users)
|
|
|
|
await ctx.send(embed=view.build_embed(), view=view)
|
|
|
|
|
|
# HELPER
|
|
def record_announcement_post(user_id: int):
|
|
|
|
leaderboard_data.record_post(user_id)
|
|
|
|
|
|
# SETUP
|
|
async def setup(bot):
|
|
|
|
await bot.add_cog(Leaderboard(bot))
|
|
|
|
|
|
|