0.3.9.2.a5
performance improvements, stability, and primarily settings-handling improvements. - Due to the container transition, some settings handling became quietly broken or defunct.
This commit is contained in:
parent
9bdb286d38
commit
5368d21be4
2
bot.py
2
bot.py
@ -8,7 +8,7 @@ from modules.common.boot_notice import post_boot_notice
|
|||||||
|
|
||||||
# Version consists of:
|
# Version consists of:
|
||||||
# Major.Enhancement.Minor.Patch.Test (Test is alphanumeric; doesn’t trigger auto update)
|
# Major.Enhancement.Minor.Patch.Test (Test is alphanumeric; doesn’t trigger auto update)
|
||||||
VERSION = "0.3.9.2.a4"
|
VERSION = "0.3.9.2.a5"
|
||||||
|
|
||||||
# ---------- Env & config loading ----------
|
# ---------- Env & config loading ----------
|
||||||
|
|
||||||
|
31
mod_perms.py
31
mod_perms.py
@ -2,26 +2,13 @@
|
|||||||
import re
|
import re
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
from modules.common.settings import cfg # ENV-first config helper
|
||||||
|
|
||||||
def _parse_ids(raw: str):
|
def _parse_ids(raw: str):
|
||||||
ids = []
|
ids = []
|
||||||
if not raw:
|
if not raw:
|
||||||
return ids
|
return ids
|
||||||
for tok in re.split(r'[,\s]+', raw.strip()):
|
for tok in re.split(r'[,\s]+', raw.strip()):
|
||||||
try:
|
|
||||||
ids.append(int(tok))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return ids
|
|
||||||
|
|
||||||
def get_mod_role_ids(bot: commands.Bot):
|
|
||||||
cfg = bot.config['DEFAULT']
|
|
||||||
# read individually; allow comma-separated in any field for flexibility
|
|
||||||
keys = ["admin_role_id", "field_mod_role_id", "intel_mod_role_id", "moderator_role_id"]
|
|
||||||
ids = []
|
|
||||||
for k in keys:
|
|
||||||
raw = cfg.get(k, "")
|
|
||||||
for tok in re.split(r"[,\s]+", raw.strip()):
|
|
||||||
if not tok:
|
if not tok:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@ -30,6 +17,22 @@ def get_mod_role_ids(bot: commands.Bot):
|
|||||||
pass
|
pass
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
|
def get_mod_role_ids(bot: commands.Bot):
|
||||||
|
# Read from ENV/INI via helper; allow comma-separated lists in any field
|
||||||
|
reader = cfg(bot)
|
||||||
|
keys = ["admin_role_id", "field_mod_role_id", "intel_mod_role_id", "moderator_role_id"]
|
||||||
|
collected = []
|
||||||
|
for k in keys:
|
||||||
|
collected.extend(_parse_ids(reader.get(k, "")))
|
||||||
|
# dedupe while preserving order
|
||||||
|
seen = set()
|
||||||
|
unique = []
|
||||||
|
for i in collected:
|
||||||
|
if i not in seen:
|
||||||
|
seen.add(i)
|
||||||
|
unique.append(i)
|
||||||
|
return unique
|
||||||
|
|
||||||
def is_moderator_member(member: discord.Member, bot: commands.Bot) -> bool:
|
def is_moderator_member(member: discord.Member, bot: commands.Bot) -> bool:
|
||||||
if not isinstance(member, discord.Member):
|
if not isinstance(member, discord.Member):
|
||||||
return False
|
return False
|
||||||
|
@ -3,10 +3,13 @@ import asyncio
|
|||||||
import time
|
import time
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
from modules.common.settings import cfg # ENV-first config helper
|
||||||
|
|
||||||
|
|
||||||
def now() -> float:
|
def now() -> float:
|
||||||
return time.time()
|
return time.time()
|
||||||
|
|
||||||
|
|
||||||
class AutoVCCog(commands.Cog):
|
class AutoVCCog(commands.Cog):
|
||||||
"""
|
"""
|
||||||
Auto-VC:
|
Auto-VC:
|
||||||
@ -23,14 +26,14 @@ class AutoVCCog(commands.Cog):
|
|||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
cfg = bot.config['DEFAULT']
|
r = cfg(bot)
|
||||||
|
|
||||||
# Config
|
# Config (ENV/INI via helper; safe defaults)
|
||||||
self.trigger_id = int(cfg['trigger_channel_id'])
|
self.trigger_id = r.int('trigger_channel_id', 0)
|
||||||
self.category_id = int(cfg['auto_vc_category_id'])
|
self.category_id = r.int('auto_vc_category_id', 0)
|
||||||
self.prefix = cfg['vc_name_prefix']
|
self.prefix = r.get('vc_name_prefix', 'Room')
|
||||||
self.delay = int(cfg.get('auto_vc_cleanup_delay', 30))
|
self.delay = r.int('auto_vc_cleanup_delay', 30)
|
||||||
self.modlog_channel_id = int(cfg.get('modlog_channel_id', '0')) if cfg.get('modlog_channel_id') else 0
|
self.modlog_channel_id = r.int('modlog_channel_id', 0)
|
||||||
|
|
||||||
# State
|
# State
|
||||||
self.empty_since: dict[int, float] = {} # channel_id -> ts when became empty
|
self.empty_since: dict[int, float] = {} # channel_id -> ts when became empty
|
||||||
@ -92,6 +95,8 @@ class AutoVCCog(commands.Cog):
|
|||||||
|
|
||||||
async def _cleanup_pass(self, guild: discord.Guild):
|
async def _cleanup_pass(self, guild: discord.Guild):
|
||||||
"""Delete empty tracked channels that exceeded delay and renumber."""
|
"""Delete empty tracked channels that exceeded delay and renumber."""
|
||||||
|
if not self.category_id:
|
||||||
|
return
|
||||||
cat = guild.get_channel(self.category_id)
|
cat = guild.get_channel(self.category_id)
|
||||||
if not cat:
|
if not cat:
|
||||||
return
|
return
|
||||||
@ -148,6 +153,9 @@ class AutoVCCog(commands.Cog):
|
|||||||
|
|
||||||
async def _spawn_and_move(self, member: discord.Member):
|
async def _spawn_and_move(self, member: discord.Member):
|
||||||
guild = member.guild
|
guild = member.guild
|
||||||
|
if not self.category_id:
|
||||||
|
await self._log(guild, "⚠️ auto_vc_category_id not configured; cannot create rooms.")
|
||||||
|
return
|
||||||
cat = guild.get_channel(self.category_id)
|
cat = guild.get_channel(self.category_id)
|
||||||
if not cat:
|
if not cat:
|
||||||
await self._log(guild, "⚠️ auto_vc_category_id not found; cannot create rooms.")
|
await self._log(guild, "⚠️ auto_vc_category_id not found; cannot create rooms.")
|
||||||
@ -195,7 +203,7 @@ class AutoVCCog(commands.Cog):
|
|||||||
guild = member.guild
|
guild = member.guild
|
||||||
|
|
||||||
# Create on trigger join (with 5s per-user cooldown)
|
# Create on trigger join (with 5s per-user cooldown)
|
||||||
if after.channel and after.channel.id == self.trigger_id:
|
if self.trigger_id and after.channel and after.channel.id == self.trigger_id:
|
||||||
last = self._vc_cooldowns.get(member.id, 0.0)
|
last = self._vc_cooldowns.get(member.id, 0.0)
|
||||||
if now() - last < 5.0:
|
if now() - last < 5.0:
|
||||||
return
|
return
|
||||||
@ -206,7 +214,7 @@ class AutoVCCog(commands.Cog):
|
|||||||
print("[auto_vc] spawn/move failed:", repr(e))
|
print("[auto_vc] spawn/move failed:", repr(e))
|
||||||
|
|
||||||
# Mark empties immediately on leave
|
# Mark empties immediately on leave
|
||||||
if before.channel:
|
if before.channel and self.category_id:
|
||||||
ch = before.channel
|
ch = before.channel
|
||||||
if ch.category_id == self.category_id:
|
if ch.category_id == self.category_id:
|
||||||
rec = self._find_record(guild.id, ch.id)
|
rec = self._find_record(guild.id, ch.id)
|
||||||
@ -221,7 +229,7 @@ class AutoVCCog(commands.Cog):
|
|||||||
g = ctx.guild
|
g = ctx.guild
|
||||||
recs = sorted(self._vc_records(g.id), key=lambda r: r.get('created_ts', 0))
|
recs = sorted(self._vc_records(g.id), key=lambda r: r.get('created_ts', 0))
|
||||||
lines = [
|
lines = [
|
||||||
f"Trigger: <#{self.trigger_id}> | Category: <#{self.category_id}> | Prefix: `{self.prefix}` | Delay: {self.delay}s"
|
f"Trigger: <#{self.trigger_id or 0}> | Category: <#{self.category_id or 0}> | Prefix: `{self.prefix}` | Delay: {self.delay}s"
|
||||||
]
|
]
|
||||||
for idx, rec in enumerate(recs, start=1):
|
for idx, rec in enumerate(recs, start=1):
|
||||||
ch = g.get_channel(rec['channel_id'])
|
ch = g.get_channel(rec['channel_id'])
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
# modules/common/boot_notice.py
|
||||||
import os
|
import os
|
||||||
import discord
|
import discord
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
from modules.common.settings import cfg
|
||||||
|
|
||||||
|
|
||||||
async def _fetch_latest_subject_sha(rss_url: str) -> tuple[str | None, str | None]:
|
async def _fetch_latest_subject_sha(rss_url: str) -> tuple[str | None, str | None]:
|
||||||
"""Best-effort: read latest commit subject + short sha from a Gitea RSS feed."""
|
"""Best-effort: read latest commit subject + short sha from a Gitea RSS feed."""
|
||||||
@ -23,6 +26,7 @@ async def _fetch_latest_subject_sha(rss_url: str) -> tuple[str | None, str | Non
|
|||||||
except Exception:
|
except Exception:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
async def post_boot_notice(bot):
|
async def post_boot_notice(bot):
|
||||||
"""
|
"""
|
||||||
Posts a boot status message to the configured modlog channel.
|
Posts a boot status message to the configured modlog channel.
|
||||||
@ -53,16 +57,15 @@ async def post_boot_notice(bot):
|
|||||||
if not line:
|
if not line:
|
||||||
return # nothing to say
|
return # nothing to say
|
||||||
|
|
||||||
try:
|
# Read modlog channel from ENV/INI via helper
|
||||||
ch_id = int(bot.config['DEFAULT'].get('modlog_channel_id', "0") or 0)
|
modlog_channel_id = cfg(bot).int('modlog_channel_id', 0)
|
||||||
except Exception:
|
if not modlog_channel_id:
|
||||||
ch_id = 0
|
|
||||||
if not ch_id:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Find channel across guilds
|
||||||
ch = None
|
ch = None
|
||||||
for g in bot.guilds:
|
for g in bot.guilds:
|
||||||
ch = g.get_channel(ch_id)
|
ch = g.get_channel(modlog_channel_id)
|
||||||
if ch:
|
if ch:
|
||||||
break
|
break
|
||||||
if not ch:
|
if not ch:
|
||||||
|
34
modules/common/settings.py
Normal file
34
modules/common/settings.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# modules/common/settings.py
|
||||||
|
import os
|
||||||
|
|
||||||
|
class ConfigView:
|
||||||
|
"""Unified read: ENV first, then optional bot.config['DEFAULT'], then fallback."""
|
||||||
|
def __init__(self, bot):
|
||||||
|
self._env = os.environ
|
||||||
|
self._default = {}
|
||||||
|
try:
|
||||||
|
self._default = getattr(bot, "config", {}).get("DEFAULT", {}) or {}
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get(self, key: str, default: str = "") -> str:
|
||||||
|
v = self._env.get(key.upper(), "")
|
||||||
|
if not v:
|
||||||
|
v = self._default.get(key, "")
|
||||||
|
v = (v or "").strip().strip('"').strip("'")
|
||||||
|
return v if v else default
|
||||||
|
|
||||||
|
def int(self, key: str, default: int = 0) -> int:
|
||||||
|
try:
|
||||||
|
return int(self.get(key, ""))
|
||||||
|
except Exception:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def bool(self, key: str, default: bool = False) -> bool:
|
||||||
|
v = self.get(key, "")
|
||||||
|
if not v:
|
||||||
|
return default
|
||||||
|
return v.lower() in ("1", "true", "yes", "on")
|
||||||
|
|
||||||
|
def cfg(bot) -> ConfigView:
|
||||||
|
return ConfigView(bot)
|
@ -1,3 +1,4 @@
|
|||||||
|
# modules/nick_nudge/nick_nudge.py
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
@ -7,6 +8,7 @@ from discord import app_commands
|
|||||||
|
|
||||||
from mod_perms import is_moderator_userid
|
from mod_perms import is_moderator_userid
|
||||||
from modules.common.emoji_accept import is_accept
|
from modules.common.emoji_accept import is_accept
|
||||||
|
from modules.common.settings import cfg # ENV-first config helper
|
||||||
|
|
||||||
CHECK = '✅' # approved/verified
|
CHECK = '✅' # approved/verified
|
||||||
CROSS = '❌' # reject / no
|
CROSS = '❌' # reject / no
|
||||||
@ -14,12 +16,14 @@ PENDING = '✔️' # heavy check mark = pending claim
|
|||||||
ACCEPT = {CHECK, '🫡'}
|
ACCEPT = {CHECK, '🫡'}
|
||||||
NO_MENTIONS = discord.AllowedMentions.none()
|
NO_MENTIONS = discord.AllowedMentions.none()
|
||||||
|
|
||||||
|
|
||||||
def _ts_rel(ts: Optional[float] = None) -> str:
|
def _ts_rel(ts: Optional[float] = None) -> str:
|
||||||
"""Discord relative timestamp like <t:12345:R>."""
|
"""Discord relative timestamp like <t:12345:R>."""
|
||||||
if ts is None:
|
if ts is None:
|
||||||
ts = time.time()
|
ts = time.time()
|
||||||
return f"<t:{int(ts)}:R>"
|
return f"<t:{int(ts)}:R>"
|
||||||
|
|
||||||
|
|
||||||
class NickNudgeCog(commands.Cog):
|
class NickNudgeCog(commands.Cog):
|
||||||
"""
|
"""
|
||||||
Handles:
|
Handles:
|
||||||
@ -32,14 +36,14 @@ class NickNudgeCog(commands.Cog):
|
|||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
cfg = bot.config['DEFAULT']
|
r = cfg(bot)
|
||||||
self.modlog_channel_id = int(cfg['modlog_channel_id'])
|
|
||||||
self.mod_channel_id = int(cfg['mod_channel_id']) # same review channel as pirate reports
|
# Config via helper (ENV -> optional INI fallback)
|
||||||
|
self.modlog_channel_id = r.int('modlog_channel_id', 0)
|
||||||
|
self.mod_channel_id = r.int('mod_channel_id', 0) # same review channel as pirate reports
|
||||||
|
|
||||||
# Optional DM nudge loop retained
|
# Optional DM nudge loop retained
|
||||||
try:
|
self.loop_enabled = r.bool('nick_nudge_loop_enabled', False)
|
||||||
self.loop_enabled = cfg.getboolean('nick_nudge_loop_enabled')
|
|
||||||
except Exception:
|
|
||||||
self.loop_enabled = False
|
|
||||||
self._task = asyncio.create_task(self._nudge_loop()) if self.loop_enabled else None
|
self._task = asyncio.create_task(self._nudge_loop()) if self.loop_enabled else None
|
||||||
|
|
||||||
# ---------- utils ----------
|
# ---------- utils ----------
|
||||||
@ -52,6 +56,8 @@ class NickNudgeCog(commands.Cog):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
async def _modlog(self, guild: discord.Guild, content: str):
|
async def _modlog(self, guild: discord.Guild, content: str):
|
||||||
|
if not self.modlog_channel_id:
|
||||||
|
return
|
||||||
ch = guild.get_channel(self.modlog_channel_id)
|
ch = guild.get_channel(self.modlog_channel_id)
|
||||||
if ch:
|
if ch:
|
||||||
try:
|
try:
|
||||||
@ -85,7 +91,7 @@ class NickNudgeCog(commands.Cog):
|
|||||||
- source: "claim" or "nick_same"
|
- source: "claim" or "nick_same"
|
||||||
Stores in data_manager['nick_reviews'] a record keyed by the review message_id.
|
Stores in data_manager['nick_reviews'] a record keyed by the review message_id.
|
||||||
"""
|
"""
|
||||||
if not guild:
|
if not guild or not self.mod_channel_id:
|
||||||
return
|
return
|
||||||
# If a pending review already exists for this user in this guild, do nothing
|
# If a pending review already exists for this user in this guild, do nothing
|
||||||
for r in self.bot.data_manager.get('nick_reviews'):
|
for r in self.bot.data_manager.get('nick_reviews'):
|
||||||
@ -135,12 +141,12 @@ class NickNudgeCog(commands.Cog):
|
|||||||
await self.bot.wait_until_ready()
|
await self.bot.wait_until_ready()
|
||||||
while not self.bot.is_closed():
|
while not self.bot.is_closed():
|
||||||
try:
|
try:
|
||||||
now = time.time()
|
now_t = time.time()
|
||||||
for guild in self.bot.guilds:
|
for guild in self.bot.guilds:
|
||||||
for member in guild.members:
|
for member in guild.members:
|
||||||
if member.bot or not member.joined_at:
|
if member.bot or not member.joined_at:
|
||||||
continue
|
continue
|
||||||
if (now - member.joined_at.timestamp()) < 24*3600:
|
if (now_t - member.joined_at.timestamp()) < 24 * 3600:
|
||||||
continue
|
continue
|
||||||
# If they already have a server nick OR already claimed/verified, skip nudging
|
# If they already have a server nick OR already claimed/verified, skip nudging
|
||||||
dm = self.bot.data_manager
|
dm = self.bot.data_manager
|
||||||
@ -162,7 +168,7 @@ class NickNudgeCog(commands.Cog):
|
|||||||
'message_id': int(msg.id),
|
'message_id': int(msg.id),
|
||||||
'user_id': int(member.id),
|
'user_id': int(member.id),
|
||||||
'guild_id': int(guild.id),
|
'guild_id': int(guild.id),
|
||||||
'ts': now
|
'ts': now_t
|
||||||
})
|
})
|
||||||
self.bot.data_manager.add('nick_nudged', int(member.id))
|
self.bot.data_manager.add('nick_nudged', int(member.id))
|
||||||
await self._modlog(guild, f"📨 Sent nickname nudge to {member.mention}")
|
await self._modlog(guild, f"📨 Sent nickname nudge to {member.mention}")
|
||||||
@ -221,7 +227,7 @@ class NickNudgeCog(commands.Cog):
|
|||||||
|
|
||||||
# 2) Handle moderator review reactions in mod channel
|
# 2) Handle moderator review reactions in mod channel
|
||||||
if payload.guild_id and str(payload.emoji) in (CHECK, CROSS) and payload.user_id != self.bot.user.id:
|
if payload.guild_id and str(payload.emoji) in (CHECK, CROSS) and payload.user_id != self.bot.user.id:
|
||||||
if payload.channel_id != self.mod_channel_id:
|
if payload.channel_id != self.mod_channel_id or not self.mod_channel_id:
|
||||||
return
|
return
|
||||||
guild = self.bot.get_guild(payload.guild_id)
|
guild = self.bot.get_guild(payload.guild_id)
|
||||||
if not guild:
|
if not guild:
|
||||||
@ -276,9 +282,7 @@ class NickNudgeCog(commands.Cog):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Modlog
|
# Modlog
|
||||||
await self._modlog(guild,
|
await self._modlog(guild, f"✅ Nickname **verified** for {member.mention} by {approver} — {_ts_rel(now_ts)}.")
|
||||||
f"✅ Nickname **verified** for {member.mention} by {approver} — {_ts_rel(now_ts)}."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Refresh roles / card
|
# Refresh roles / card
|
||||||
rr = self.bot.get_cog('ReactionRoleCog')
|
rr = self.bot.get_cog('ReactionRoleCog')
|
||||||
@ -313,9 +317,7 @@ class NickNudgeCog(commands.Cog):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
await self._modlog(guild,
|
await self._modlog(guild, f"❌ Nickname **rejected** for {member.mention} by {approver} — {_ts_rel(now_ts)}.")
|
||||||
f"❌ Nickname **rejected** for {member.mention} by {approver} — {_ts_rel(now_ts)}."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Refresh roles / card
|
# Refresh roles / card
|
||||||
rr = self.bot.get_cog('ReactionRoleCog')
|
rr = self.bot.get_cog('ReactionRoleCog')
|
||||||
@ -331,5 +333,6 @@ class NickNudgeCog(commands.Cog):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
async def setup(bot):
|
||||||
await bot.add_cog(NickNudgeCog(bot))
|
await bot.add_cog(NickNudgeCog(bot))
|
||||||
|
@ -3,24 +3,22 @@ import asyncio
|
|||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from mod_perms import require_mod_ctx # use your configured moderator roles
|
from mod_perms import require_mod_ctx
|
||||||
|
from modules.common.settings import cfg # ENV-first config helper
|
||||||
|
|
||||||
|
|
||||||
class PirateCardsCog(commands.Cog):
|
class PirateCardsCog(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
cfg = bot.config['DEFAULT']
|
r = cfg(bot)
|
||||||
self.pirates_channel_id = int(cfg['pirates_list_channel_id'])
|
|
||||||
self.modlog_channel_id = int(cfg.get('modlog_channel_id', '0')) if cfg.get('modlog_channel_id') else 0
|
|
||||||
|
|
||||||
# thresholds / samples (optional, with defaults)
|
# IDs / config (ENV -> optional INI fallback)
|
||||||
try:
|
self.pirates_channel_id = r.int('pirates_list_channel_id', 0)
|
||||||
self.group_threshold = int(cfg.get('threat_group_threshold', '3'))
|
self.modlog_channel_id = r.int('modlog_channel_id', 0)
|
||||||
except Exception:
|
|
||||||
self.group_threshold = 3
|
# thresholds / samples (with defaults)
|
||||||
try:
|
self.group_threshold = r.int('threat_group_threshold', 3)
|
||||||
self.min_samples = int(cfg.get('threat_min_samples_for_stats', '3'))
|
self.min_samples = r.int('threat_min_samples_for_stats', 3)
|
||||||
except Exception:
|
|
||||||
self.min_samples = 3
|
|
||||||
|
|
||||||
# safe posting (don’t ping)
|
# safe posting (don’t ping)
|
||||||
self._no_mentions = discord.AllowedMentions.none()
|
self._no_mentions = discord.AllowedMentions.none()
|
||||||
@ -97,7 +95,7 @@ class PirateCardsCog(commands.Cog):
|
|||||||
async def _build_embed(self, pirate: dict) -> discord.Embed:
|
async def _build_embed(self, pirate: dict) -> discord.Embed:
|
||||||
encs = self._encounters_for(pirate)
|
encs = self._encounters_for(pirate)
|
||||||
total = len(encs)
|
total = len(encs)
|
||||||
# guard numeric fields
|
|
||||||
def _i(v, d=0):
|
def _i(v, d=0):
|
||||||
try:
|
try:
|
||||||
return int(v)
|
return int(v)
|
||||||
@ -222,5 +220,6 @@ class PirateCardsCog(commands.Cog):
|
|||||||
is_slash = hasattr(ctx, "interaction") and ctx.interaction is not None
|
is_slash = hasattr(ctx, "interaction") and ctx.interaction is not None
|
||||||
await ctx.reply(f"Rebuilt/updated {count} pirate cards.", ephemeral=is_slash)
|
await ctx.reply(f"Rebuilt/updated {count} pirate cards.", ephemeral=is_slash)
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
async def setup(bot):
|
||||||
await bot.add_cog(PirateCardsCog(bot))
|
await bot.add_cog(PirateCardsCog(bot))
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
# modules/pirate_report/pirate_report.py
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
|
from modules.common.settings import cfg
|
||||||
|
|
||||||
from mod_perms import (
|
from mod_perms import (
|
||||||
is_moderator_member,
|
is_moderator_member,
|
||||||
@ -341,20 +343,25 @@ class EncounterModal(discord.ui.Modal, title="Log Pirate Encounter"):
|
|||||||
class PirateReportCog(commands.Cog):
|
class PirateReportCog(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
cfg = bot.config['DEFAULT']
|
r = cfg(bot)
|
||||||
self.mod_channel = int(cfg['mod_channel_id'])
|
|
||||||
self.modlog_channel_id = int(cfg['modlog_channel_id'])
|
|
||||||
|
|
||||||
# Optional threat weights (normalized elsewhere if you added them)
|
# Channels
|
||||||
|
self.mod_channel = r.int('mod_channel_id', 0)
|
||||||
|
self.modlog_channel_id = r.int('modlog_channel_id', 0)
|
||||||
|
|
||||||
|
# Threat config
|
||||||
|
self.group_threshold = r.int('threat_group_threshold', 3)
|
||||||
|
|
||||||
|
def _f(key: str, default: float) -> float:
|
||||||
try:
|
try:
|
||||||
self.group_threshold = int(cfg.get('threat_group_threshold', '3'))
|
return float(r.get(key, str(default)))
|
||||||
except Exception:
|
except Exception:
|
||||||
self.group_threshold = 3
|
return default
|
||||||
# Defaults if not already present in your earlier version:
|
|
||||||
self.w_kill = float(cfg.get('threat_w_kill', '0.35'))
|
self.w_kill = _f('threat_w_kill', 0.35)
|
||||||
self.w_destruction = float(cfg.get('threat_w_destruction', '0.30'))
|
self.w_destruction = _f('threat_w_destruction', 0.30)
|
||||||
self.w_group = float(cfg.get('threat_w_group', '0.20'))
|
self.w_group = _f('threat_w_group', 0.20)
|
||||||
self.w_skill = float(cfg.get('threat_w_skill', '0.15'))
|
self.w_skill = _f('threat_w_skill', 0.15)
|
||||||
|
|
||||||
async def _refresh_pirates_list(self, guild: discord.Guild):
|
async def _refresh_pirates_list(self, guild: discord.Guild):
|
||||||
plist = self.bot.get_cog('PiratesListCog')
|
plist = self.bot.get_cog('PiratesListCog')
|
||||||
@ -362,6 +369,8 @@ class PirateReportCog(commands.Cog):
|
|||||||
await plist.refresh_list(guild)
|
await plist.refresh_list(guild)
|
||||||
|
|
||||||
async def _modlog(self, guild: discord.Guild, content: str):
|
async def _modlog(self, guild: discord.Guild, content: str):
|
||||||
|
if not self.modlog_channel_id:
|
||||||
|
return
|
||||||
ch = guild.get_channel(self.modlog_channel_id)
|
ch = guild.get_channel(self.modlog_channel_id)
|
||||||
if ch:
|
if ch:
|
||||||
try:
|
try:
|
||||||
@ -563,11 +572,8 @@ async def setup(bot):
|
|||||||
cog = PirateReportCog(bot)
|
cog = PirateReportCog(bot)
|
||||||
await bot.add_cog(cog)
|
await bot.add_cog(cog)
|
||||||
|
|
||||||
try:
|
# Register commands either globally or to a specific guild if configured
|
||||||
home_gid = int(bot.config['DEFAULT'].get('home_guild_id', '0'))
|
home_gid = cfg(bot).int('home_guild_id', 0)
|
||||||
except Exception:
|
|
||||||
home_gid = 0
|
|
||||||
|
|
||||||
if home_gid:
|
if home_gid:
|
||||||
guild_obj = discord.Object(id=home_gid)
|
guild_obj = discord.Object(id=home_gid)
|
||||||
bot.tree.add_command(cog.report, guild=guild_obj)
|
bot.tree.add_command(cog.report, guild=guild_obj)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
# modules/reaction_role/reaction_role.py
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from modules.common.emoji_accept import is_accept
|
from modules.common.emoji_accept import is_accept
|
||||||
|
from modules.common.settings import cfg # ENV-first helper
|
||||||
|
|
||||||
CHECKMARK = '✅'
|
CHECKMARK = '✅'
|
||||||
|
|
||||||
@ -16,21 +18,16 @@ class ReactionRoleCog(commands.Cog):
|
|||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
cfg = bot.config['DEFAULT']
|
r = cfg(bot)
|
||||||
|
|
||||||
def _i(key):
|
# Message + role IDs from ENV/INI (default 0 if unset)
|
||||||
try:
|
self.rules_msg_id = r.int('rules_message_id', 0)
|
||||||
v = cfg.get(key)
|
self.engage_msg_id = r.int('engagement_message_id', 0)
|
||||||
return int(v) if v else 0
|
self.nick_msg_id = r.int('nickname_message_id', 0)
|
||||||
except Exception:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
self.rules_msg_id = _i('rules_message_id')
|
self.rules_role = r.int('rules_role_id', 0)
|
||||||
self.engage_msg_id = _i('engagement_message_id')
|
self.engage_role = r.int('engagement_role_id', 0)
|
||||||
self.nick_msg_id = _i('nickname_message_id')
|
self.full_access_role = r.int('full_access_role_id', 0)
|
||||||
self.rules_role = _i('rules_role_id')
|
|
||||||
self.engage_role = _i('engagement_role_id')
|
|
||||||
self.full_access_role = _i('full_access_role_id')
|
|
||||||
|
|
||||||
# ---- helpers ----
|
# ---- helpers ----
|
||||||
def _has_rules(self, member_id: int) -> bool:
|
def _has_rules(self, member_id: int) -> bool:
|
||||||
@ -215,5 +212,6 @@ class ReactionRoleCog(commands.Cog):
|
|||||||
|
|
||||||
await self.maybe_apply_full_access(member)
|
await self.maybe_apply_full_access(member)
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
async def setup(bot):
|
||||||
await bot.add_cog(ReactionRoleCog(bot))
|
await bot.add_cog(ReactionRoleCog(bot))
|
||||||
|
@ -6,6 +6,7 @@ from typing import List, Dict, Tuple, Optional
|
|||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
|
from modules.common.settings import cfg
|
||||||
|
|
||||||
# Accept both for backward compatibility; display uses "Refiner"
|
# Accept both for backward compatibility; display uses "Refiner"
|
||||||
VALID_ROLES = {"crawler_owner", "carrier_owner", "refiner_owner", "lsr_owner"}
|
VALID_ROLES = {"crawler_owner", "carrier_owner", "refiner_owner", "lsr_owner"}
|
||||||
@ -560,12 +561,12 @@ class _StartView(discord.ui.View):
|
|||||||
class SpicePayCog(commands.Cog):
|
class SpicePayCog(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.sessions: Dict[tuple, Dict] = {}
|
self-sessions: Dict[tuple, Dict] = {}
|
||||||
|
|
||||||
cfg = bot.config['DEFAULT']
|
r = cfg(bot)
|
||||||
def _f(key, default):
|
def _f(key, default):
|
||||||
try:
|
try:
|
||||||
return float(cfg.get(key, str(default)))
|
return float(r.get(key, str(default)))
|
||||||
except Exception:
|
except Exception:
|
||||||
return float(default)
|
return float(default)
|
||||||
self.base_weight = _f('spicepay_base_weight', 25.0)
|
self.base_weight = _f('spicepay_base_weight', 25.0)
|
||||||
@ -575,7 +576,8 @@ class SpicePayCog(commands.Cog):
|
|||||||
|
|
||||||
def _i(key):
|
def _i(key):
|
||||||
try:
|
try:
|
||||||
return int(cfg.get(key)) if cfg.get(key) else None
|
v = r.get(key, "")
|
||||||
|
return int(v) if v else None
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
self.emoji_sand_id = _i('emoji_sand_id')
|
self.emoji_sand_id = _i('emoji_sand_id')
|
||||||
@ -679,7 +681,7 @@ class SpicePayCog(commands.Cog):
|
|||||||
f"- Base weight: **{self.base_weight} × active %**\n"
|
f"- Base weight: **{self.base_weight} × active %**\n"
|
||||||
f"- Carrier bonus: **+{self.carrier_bonus}**\n"
|
f"- Carrier bonus: **+{self.carrier_bonus}**\n"
|
||||||
f"- Crawler bonus: **+{self.crawler_bonus}**\n\n"
|
f"- Crawler bonus: **+{self.crawler_bonus}**\n\n"
|
||||||
"_Edit these in `settings.conf` under `[DEFAULT]` and restart the bot._"
|
"_Set via environment variables or your INI. Restart the bot after changing._"
|
||||||
)
|
)
|
||||||
await interaction.response.send_message(txt, ephemeral=True)
|
await interaction.response.send_message(txt, ephemeral=True)
|
||||||
|
|
||||||
@ -899,11 +901,7 @@ async def setup(bot):
|
|||||||
cog = SpicePayCog(bot)
|
cog = SpicePayCog(bot)
|
||||||
await bot.add_cog(cog)
|
await bot.add_cog(cog)
|
||||||
|
|
||||||
try:
|
home_gid = cfg(bot).int('home_guild_id', 0)
|
||||||
home_gid = int(bot.config['DEFAULT'].get('home_guild_id', '0'))
|
|
||||||
except Exception:
|
|
||||||
home_gid = 0
|
|
||||||
|
|
||||||
if home_gid:
|
if home_gid:
|
||||||
guild_obj = discord.Object(id=home_gid)
|
guild_obj = discord.Object(id=home_gid)
|
||||||
bot.tree.add_command(cog.spicepay, guild=guild_obj)
|
bot.tree.add_command(cog.spicepay, guild=guild_obj)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
# modules/user_cards/user_cards.py
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Set, Tuple
|
from typing import Optional, Set, Tuple
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from modules.common.emoji_accept import is_accept
|
from modules.common.emoji_accept import is_accept
|
||||||
|
from modules.common.settings import cfg # ENV-first helper
|
||||||
|
|
||||||
CHECK = '✅' # verified
|
CHECK = '✅' # verified
|
||||||
CROSS = '❌' # not done
|
CROSS = '❌' # not done
|
||||||
@ -11,6 +13,7 @@ PENDING = '✔️' # claimed / pending review
|
|||||||
ACCEPT = {CHECK, '🫡'}
|
ACCEPT = {CHECK, '🫡'}
|
||||||
NO_MENTIONS = discord.AllowedMentions.none()
|
NO_MENTIONS = discord.AllowedMentions.none()
|
||||||
|
|
||||||
|
|
||||||
class UserCardsCog(commands.Cog):
|
class UserCardsCog(commands.Cog):
|
||||||
"""
|
"""
|
||||||
Per-user status cards with live reconcile and offline review triggers.
|
Per-user status cards with live reconcile and offline review triggers.
|
||||||
@ -25,34 +28,35 @@ class UserCardsCog(commands.Cog):
|
|||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
cfg = bot.config['DEFAULT']
|
r = cfg(bot)
|
||||||
self.userslist_channel_id = int(cfg['userslist_channel_id'])
|
|
||||||
self.modlog_channel_id = int(cfg['modlog_channel_id'])
|
# Channels / IDs from ENV/INI
|
||||||
self.mod_channel_id = int(cfg.get('mod_channel_id', '0') or 0)
|
self.userslist_channel_id = r.int('userslist_channel_id', 0)
|
||||||
|
self.modlog_channel_id = r.int('modlog_channel_id', 0)
|
||||||
|
self.mod_channel_id = r.int('mod_channel_id', 0)
|
||||||
|
|
||||||
# reaction-role authoritative messages/roles
|
# reaction-role authoritative messages/roles
|
||||||
self.rules_msg_id = int(cfg['rules_message_id'])
|
self.rules_msg_id = r.int('rules_message_id', 0)
|
||||||
self.engage_msg_id = int(cfg['engagement_message_id'])
|
self.engage_msg_id = r.int('engagement_message_id', 0)
|
||||||
self.nick_msg_id = int(cfg['nickname_message_id'])
|
self.nick_msg_id = r.int('nickname_message_id', 0)
|
||||||
self.rules_role_id = int(cfg['rules_role_id'])
|
self.rules_role_id = r.int('rules_role_id', 0)
|
||||||
self.engage_role_id = int(cfg['engagement_role_id'])
|
self.engage_role_id = r.int('engagement_role_id', 0)
|
||||||
self.full_access_role_id = int(cfg['full_access_role_id'])
|
self.full_access_role_id = r.int('full_access_role_id', 0)
|
||||||
|
|
||||||
self._refresh_locks = {} # per-user locks to avoid racey double-posts
|
self._refresh_locks = {} # per-user locks to avoid racey double-posts
|
||||||
|
|
||||||
# Optional periodic refresh (twice a day)
|
# Optional periodic refresh (twice a day)
|
||||||
try:
|
self.cron_enabled = r.bool('user_cards_cron_enabled', False)
|
||||||
self.cron_enabled = cfg.getboolean('user_cards_cron_enabled')
|
|
||||||
except Exception:
|
|
||||||
self.cron_enabled = False
|
|
||||||
|
|
||||||
self._cron_task = asyncio.create_task(self._periodic_refresh()) if self.cron_enabled else None
|
self._cron_task = asyncio.create_task(self._periodic_refresh()) if self.cron_enabled else None
|
||||||
self._startup_task = asyncio.create_task(self._startup_reconcile())
|
self._startup_task = asyncio.create_task(self._startup_reconcile())
|
||||||
|
|
||||||
def cog_unload(self):
|
def cog_unload(self):
|
||||||
for t in (self._cron_task, self._startup_task):
|
for t in (self._cron_task, self._startup_task):
|
||||||
if t:
|
if t:
|
||||||
try: t.cancel()
|
try:
|
||||||
except Exception: pass
|
t.cancel()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# ---------- status helpers ----------
|
# ---------- status helpers ----------
|
||||||
|
|
||||||
@ -126,7 +130,7 @@ class UserCardsCog(commands.Cog):
|
|||||||
if member.avatar:
|
if member.avatar:
|
||||||
embed.set_thumbnail(url=member.avatar.url)
|
embed.set_thumbnail(url=member.avatar.url)
|
||||||
|
|
||||||
# NEW: stable identity so we can find/edit the right card later
|
# Stable identity so we can find/edit the right card later
|
||||||
embed.set_footer(text=f"UID:{member.id}")
|
embed.set_footer(text=f"UID:{member.id}")
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
@ -137,7 +141,7 @@ class UserCardsCog(commands.Cog):
|
|||||||
2) If not found, search the channel by footer marker and edit that.
|
2) If not found, search the channel by footer marker and edit that.
|
||||||
3) If still not found, post a new one, then delete any stragglers with the same marker.
|
3) If still not found, post a new one, then delete any stragglers with the same marker.
|
||||||
"""
|
"""
|
||||||
if not member or not member.guild:
|
if not member or not member.guild or not self.userslist_channel_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
async with self._lock_for(member.id):
|
async with self._lock_for(member.id):
|
||||||
@ -183,11 +187,13 @@ class UserCardsCog(commands.Cog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 4) Post fresh card
|
# 4) Post fresh card
|
||||||
|
try:
|
||||||
new_msg = await channel.send(embed=embed, allowed_mentions=NO_MENTIONS)
|
new_msg = await channel.send(embed=embed, allowed_mentions=NO_MENTIONS)
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
|
||||||
# 5) Clean up any other messages that look like this user's card
|
# 5) Clean up any other messages that look like this user's card
|
||||||
try:
|
try:
|
||||||
# Find any *other* occurrences with the same footer marker and delete them
|
|
||||||
marker = f"UID:{member.id}"
|
marker = f"UID:{member.id}"
|
||||||
async for m in channel.history(limit=400, oldest_first=False):
|
async for m in channel.history(limit=400, oldest_first=False):
|
||||||
if m.id == new_msg.id or m.author.id != self.bot.user.id or not m.embeds:
|
if m.id == new_msg.id or m.author.id != self.bot.user.id or not m.embeds:
|
||||||
@ -229,10 +235,14 @@ class UserCardsCog(commands.Cog):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
async def _log(self, guild: discord.Guild, content: str):
|
async def _log(self, guild: discord.Guild, content: str):
|
||||||
|
if not self.modlog_channel_id:
|
||||||
|
return
|
||||||
ch = guild.get_channel(self.modlog_channel_id)
|
ch = guild.get_channel(self.modlog_channel_id)
|
||||||
if ch:
|
if ch:
|
||||||
try: await ch.send(content, allowed_mentions=NO_MENTIONS)
|
try:
|
||||||
except Exception: pass
|
await ch.send(content, allowed_mentions=NO_MENTIONS)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
self.bot.data_manager.add('modlog', {'guild_id': guild.id, 'content': content})
|
self.bot.data_manager.add('modlog', {'guild_id': guild.id, 'content': content})
|
||||||
|
|
||||||
# ---------- RR message lookup & reactor collection ----------
|
# ---------- RR message lookup & reactor collection ----------
|
||||||
@ -249,6 +259,8 @@ class UserCardsCog(commands.Cog):
|
|||||||
dm.add('rr_msg_channels', {'guild_id': guild_id, 'message_id': int(message_id), 'channel_id': int(channel_id)})
|
dm.add('rr_msg_channels', {'guild_id': guild_id, 'message_id': int(message_id), 'channel_id': int(channel_id)})
|
||||||
|
|
||||||
async def _get_message_by_id(self, guild: discord.Guild, message_id: int) -> Optional[discord.Message]:
|
async def _get_message_by_id(self, guild: discord.Guild, message_id: int) -> Optional[discord.Message]:
|
||||||
|
if not message_id:
|
||||||
|
return None
|
||||||
ch_id = self._get_cached_msg_channel_id(guild.id, message_id)
|
ch_id = self._get_cached_msg_channel_id(guild.id, message_id)
|
||||||
if ch_id:
|
if ch_id:
|
||||||
ch = guild.get_channel(ch_id)
|
ch = guild.get_channel(ch_id)
|
||||||
@ -362,7 +374,7 @@ class UserCardsCog(commands.Cog):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# --- New part: open reviews for *any* unreviewed claimers (startup/offline) ---
|
# --- Open reviews for *any* unreviewed claimers (startup/offline) ---
|
||||||
nn = self.bot.get_cog('NickNudgeCog')
|
nn = self.bot.get_cog('NickNudgeCog')
|
||||||
|
|
||||||
verified_set = set(dm.get('nick_verified'))
|
verified_set = set(dm.get('nick_verified'))
|
||||||
@ -431,7 +443,7 @@ class UserCardsCog(commands.Cog):
|
|||||||
await self._log(member.guild, f"📝 User joined: {member.mention} (ID: {member.id})")
|
await self._log(member.guild, f"📝 User joined: {member.mention} (ID: {member.id})")
|
||||||
await self.refresh_card(member)
|
await self.refresh_card(member)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener())
|
||||||
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
||||||
if before.nick != after.nick or before.roles != after.roles:
|
if before.nick != after.nick or before.roles != after.roles:
|
||||||
await self.refresh_card(after)
|
await self.refresh_card(after)
|
||||||
@ -443,8 +455,10 @@ class UserCardsCog(commands.Cog):
|
|||||||
for g in self.bot.guilds:
|
for g in self.bot.guilds:
|
||||||
m = g.get_member(after.id)
|
m = g.get_member(after.id)
|
||||||
if m:
|
if m:
|
||||||
try: await self.refresh_card(m)
|
try:
|
||||||
except Exception: pass
|
await self.refresh_card(m)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# ---------- periodic + startup ----------
|
# ---------- periodic + startup ----------
|
||||||
|
|
||||||
@ -458,16 +472,20 @@ class UserCardsCog(commands.Cog):
|
|||||||
for g in list(self.bot.guilds):
|
for g in list(self.bot.guilds):
|
||||||
for m in g.members:
|
for m in g.members:
|
||||||
if not m.bot:
|
if not m.bot:
|
||||||
try: await self.refresh_card(m)
|
try:
|
||||||
except Exception: pass
|
await self.refresh_card(m)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
async def _periodic_refresh(self):
|
async def _periodic_refresh(self):
|
||||||
await self.bot.wait_until_ready()
|
await self.bot.wait_until_ready()
|
||||||
while not self.bot.is_closed():
|
while not self.bot.is_closed():
|
||||||
try:
|
try:
|
||||||
for g in self.bot.guilds:
|
for g in self.bot.guilds:
|
||||||
try: await self._reconcile_agreements(g)
|
try:
|
||||||
except Exception: pass
|
await self._reconcile_agreements(g)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
for m in g.members:
|
for m in g.members:
|
||||||
if not m.bot:
|
if not m.bot:
|
||||||
await self.refresh_card(m)
|
await self.refresh_card(m)
|
||||||
@ -506,5 +524,6 @@ class UserCardsCog(commands.Cog):
|
|||||||
ephemeral=True
|
ephemeral=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
async def setup(bot):
|
||||||
await bot.add_cog(UserCardsCog(bot))
|
await bot.add_cog(UserCardsCog(bot))
|
||||||
|
Loading…
Reference in New Issue
Block a user