0.3.9.7.a1
Added random presence rotator, because... fun!
This commit is contained in:
		
							parent
							
								
									eb1e1da82f
								
							
						
					
					
						commit
						8fb7a9dab5
					
				
							
								
								
									
										2
									
								
								bot.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								bot.py
									
									
									
									
									
								
							@ -9,7 +9,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.6.a5"
 | 
					VERSION = "0.3.9.7.a1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ---------- Env loading ----------
 | 
					# ---------- Env loading ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										0
									
								
								modules/status/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								modules/status/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										318
									
								
								modules/status/status_rotator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								modules/status/status_rotator.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,318 @@
 | 
				
			|||||||
 | 
					# modules/status/status_rotator.py
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					from datetime import datetime, timezone, timedelta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import discord
 | 
				
			||||||
 | 
					from discord.ext import commands, tasks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from modules.common.settings import cfg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ============== Tunables / lists you can expand freely ==============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Long list of expressive unicode emojis (safe for all clients)
 | 
				
			||||||
 | 
					EMOTES = [
 | 
				
			||||||
 | 
					    "❤️","🧡","💛","💚","💙","💜","🤎","🖤","🤍","✨","🌟","⭐","🎉","🎊","🔥","💫","⚡","🌈",
 | 
				
			||||||
 | 
					    "😄","😁","😆","😊","🙂","😉","😎","🤩","🥳","🤗","🙌","👏","👍","🤝","🫶","🙏","🫡","🤘","💪",
 | 
				
			||||||
 | 
					    "👀","🤔","🧐","😼","😹","😏","😌","😇","😴","🤖","👾","🧠","🫠",
 | 
				
			||||||
 | 
					    "🏜️","🌵","🐪","🐛","🪱","🧂","🧪","🗡️","⚔️","🛡️","🚁","🛩️","🚀","🧭","🌪️"
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Short Dune phrases / lore snippets (kept short for presence)
 | 
				
			||||||
 | 
					DUNE_PHRASES = [
 | 
				
			||||||
 | 
					    "Arrakis. Dune. Desert Planet.",
 | 
				
			||||||
 | 
					    "Shai-Hulud stirs beneath the sands.",
 | 
				
			||||||
 | 
					    "The spice must flow.",
 | 
				
			||||||
 | 
					    "Bene Gesserit whispers in the dark.",
 | 
				
			||||||
 | 
					    "Kwisatz Haderach foretold.",
 | 
				
			||||||
 | 
					    "House Atreides rises.",
 | 
				
			||||||
 | 
					    "House Harkonnen plots.",
 | 
				
			||||||
 | 
					    "Fremen walk without rhythm.",
 | 
				
			||||||
 | 
					    "Crysknife unsheathed.",
 | 
				
			||||||
 | 
					    "Sietch Tabr stands strong.",
 | 
				
			||||||
 | 
					    "CHOAM counts its profits.",
 | 
				
			||||||
 | 
					    "The Spacing Guild navigates the void.",
 | 
				
			||||||
 | 
					    "Water is life.",
 | 
				
			||||||
 | 
					    "Fear is the mind-killer.",
 | 
				
			||||||
 | 
					    "Gom Jabbar at the throat.",
 | 
				
			||||||
 | 
					    "Stillsuits conserve every drop.",
 | 
				
			||||||
 | 
					    "Ornithopters in the storm.",
 | 
				
			||||||
 | 
					    "Sardaukar march.",
 | 
				
			||||||
 | 
					    "Prescience veils the future.",
 | 
				
			||||||
 | 
					    "Fedāykin watchful in the dunes."
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Concise fun facts (will be numbered). Keep them reasonably short.
 | 
				
			||||||
 | 
					FUN_FACTS = [
 | 
				
			||||||
 | 
					    "Frank Herbert conceived Dune after reporting on sand dune stabilization in the Oregon coast.",
 | 
				
			||||||
 | 
					    "‘Muad’Dib’ is a small desert mouse whose footprints taught Paul the sandwalk.",
 | 
				
			||||||
 | 
					    "Melange extends life, heightens awareness, and enables prescient navigation.",
 | 
				
			||||||
 | 
					    "Fremen ‘walking without rhythm’ avoids attracting sandworms.",
 | 
				
			||||||
 | 
					    "The crysknife is made from a sandworm’s tooth and must never be sheathed unblooded.",
 | 
				
			||||||
 | 
					    "Spacing Guild Navigators require spice to safely fold space.",
 | 
				
			||||||
 | 
					    "The Litany Against Fear is recited to focus and master one’s emotions.",
 | 
				
			||||||
 | 
					    "Bene Gesserit use prana-bindu training to control every muscle fiber.",
 | 
				
			||||||
 | 
					    "Sietch means a Fremen community cave complex.",
 | 
				
			||||||
 | 
					    "Stillsuits can reclaim over 90% of the body’s moisture.",
 | 
				
			||||||
 | 
					    "The Imperial throne balances the Landsraad, CHOAM, and the Guild.",
 | 
				
			||||||
 | 
					    "Kanly refers to a formal vendetta between noble houses.",
 | 
				
			||||||
 | 
					    "Arrakis once had open water—long before the events of Dune.",
 | 
				
			||||||
 | 
					    "‘Shai-Hulud’ is the Fremen name for the grand sandworm.",
 | 
				
			||||||
 | 
					    "A lasgun-shield interaction can cause a subatomic chain reaction.",
 | 
				
			||||||
 | 
					    "Mentats are human computers trained to replace forbidden thinking machines.",
 | 
				
			||||||
 | 
					    "Duncan Idaho appears across eras via ghola rebirths.",
 | 
				
			||||||
 | 
					    "The Water of Life is a deadly spice exhalation—surviving it transforms the Reverend Mother.",
 | 
				
			||||||
 | 
					    "Fedaykin are the Fremen elite commandos sworn to their leader.",
 | 
				
			||||||
 | 
					    "Sandtrout are juvenile forms of the great sandworms.",
 | 
				
			||||||
 | 
					    "The Butlerian Jihad outlawed thinking machines, reshaping human society.",
 | 
				
			||||||
 | 
					    "Caladan is the Atreides ocean world before their move to Arrakis.",
 | 
				
			||||||
 | 
					    "The Harkonnen homeworld is Giedi Prime, an industrialized, harsh planet.",
 | 
				
			||||||
 | 
					    "‘He who controls the spice controls the universe.’",
 | 
				
			||||||
 | 
					    "The Weirding Way is a Bene Gesserit martial art emphasizing speed and economy."
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ============== Cog implementation ==============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StatusRotatorCog(commands.Cog):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Cycles bot presence every N minutes with randomized status entries:
 | 
				
			||||||
 | 
					      - Member count (excluding bots)
 | 
				
			||||||
 | 
					      - Pirates count
 | 
				
			||||||
 | 
					      - Encounters count
 | 
				
			||||||
 | 
					      - Random shout-out: {emoji} {display_name} (members with SHAI_FULL_ACCESS_ROLE_ID)
 | 
				
			||||||
 | 
					      - Count of fully initiated members (role members)
 | 
				
			||||||
 | 
					      - Random Dune phrase
 | 
				
			||||||
 | 
					      - Random Dune fun fact (#n -> fact)
 | 
				
			||||||
 | 
					      - Top threat pirate
 | 
				
			||||||
 | 
					      - Latest report (pending/most recent)
 | 
				
			||||||
 | 
					      - Encounters in last 24h
 | 
				
			||||||
 | 
					      - Uptime
 | 
				
			||||||
 | 
					      - Current running version
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, bot: commands.Bot):
 | 
				
			||||||
 | 
					        self.bot = bot
 | 
				
			||||||
 | 
					        r = cfg(bot)
 | 
				
			||||||
 | 
					        self.home_gid = r.int('home_guild_id', 0)
 | 
				
			||||||
 | 
					        self.full_access_role_id = r.int('full_access_role_id', 0)  # SHAI_FULL_ACCESS_ROLE_ID
 | 
				
			||||||
 | 
					        self.interval_min = r.int('status_interval_min', 5)         # SHAI_STATUS_INTERVAL_MIN (optional)
 | 
				
			||||||
 | 
					        self.max_len = 120  # clip safety for PC clients (keeps presence tidy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Build generator list (enable/disable here if needed)
 | 
				
			||||||
 | 
					        self._generators = [
 | 
				
			||||||
 | 
					            self._gen_members_count,
 | 
				
			||||||
 | 
					            self._gen_pirates_count,
 | 
				
			||||||
 | 
					            self._gen_encounters_count,
 | 
				
			||||||
 | 
					            self._gen_random_shoutout,
 | 
				
			||||||
 | 
					            self._gen_initiated_count,
 | 
				
			||||||
 | 
					            self._gen_random_phrase,
 | 
				
			||||||
 | 
					            self._gen_random_fun_fact,
 | 
				
			||||||
 | 
					            self._gen_top_threat,
 | 
				
			||||||
 | 
					            self._gen_latest_report,
 | 
				
			||||||
 | 
					            self._gen_encounters_last_24h,
 | 
				
			||||||
 | 
					            self._gen_uptime,
 | 
				
			||||||
 | 
					            self._gen_running_version,
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        self._queue = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # ---- lifecycle ----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @commands.Cog.listener()
 | 
				
			||||||
 | 
					    async def on_ready(self):
 | 
				
			||||||
 | 
					        # ensure loop interval reflects config
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            if self.rotate_status.is_running():
 | 
				
			||||||
 | 
					                self.rotate_status.change_interval(minutes=max(1, self.interval_min))
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.rotate_status.change_interval(minutes=max(1, self.interval_min))
 | 
				
			||||||
 | 
					                self.rotate_status.start()
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            print("[status] failed to start/change loop:", repr(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def cog_unload(self):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self.rotate_status.cancel()
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # ---- the loop ----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @tasks.loop(minutes=5)
 | 
				
			||||||
 | 
					    async def rotate_status(self):
 | 
				
			||||||
 | 
					        await self.bot.wait_until_ready()
 | 
				
			||||||
 | 
					        guild = self._resolve_guild()
 | 
				
			||||||
 | 
					        if not guild:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        text = await self._next_status_text(guild)
 | 
				
			||||||
 | 
					        if not text:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        atype = random.choice([
 | 
				
			||||||
 | 
					            discord.ActivityType.watching,
 | 
				
			||||||
 | 
					            discord.ActivityType.playing,
 | 
				
			||||||
 | 
					            discord.ActivityType.listening
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            await self.bot.change_presence(activity=discord.Activity(type=atype, name=text))
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            print("[status] change_presence failed:", repr(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # ---- helpers ----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _resolve_guild(self) -> discord.Guild | None:
 | 
				
			||||||
 | 
					        if self.home_gid:
 | 
				
			||||||
 | 
					            g = self.bot.get_guild(self.home_gid)
 | 
				
			||||||
 | 
					            if g:
 | 
				
			||||||
 | 
					                return g
 | 
				
			||||||
 | 
					        return self.bot.guilds[0] if self.bot.guilds else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _next_status_text(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        # Try up to len(generators) times to get a non-empty status
 | 
				
			||||||
 | 
					        for _ in range(len(self._generators)):
 | 
				
			||||||
 | 
					            if not self._queue:
 | 
				
			||||||
 | 
					                self._queue = random.sample(self._generators, k=len(self._generators))
 | 
				
			||||||
 | 
					            gen = self._queue.pop(0)
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                s = await gen(guild)
 | 
				
			||||||
 | 
					                s = self._clip(s)
 | 
				
			||||||
 | 
					                if s:
 | 
				
			||||||
 | 
					                    return s
 | 
				
			||||||
 | 
					            except Exception as e:
 | 
				
			||||||
 | 
					                print(f"[status] generator {gen.__name__} failed:", repr(e))
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _clip(self, s: str | None) -> str | None:
 | 
				
			||||||
 | 
					        if not s:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        s = s.strip()
 | 
				
			||||||
 | 
					        return (s[: self.max_len - 1] + "…") if len(s) > self.max_len else s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def _fmt_duration(seconds: float) -> str:
 | 
				
			||||||
 | 
					        seconds = int(max(0, seconds))
 | 
				
			||||||
 | 
					        d, rem = divmod(seconds, 86400)
 | 
				
			||||||
 | 
					        h, rem = divmod(rem, 3600)
 | 
				
			||||||
 | 
					        m, _ = divmod(rem, 60)
 | 
				
			||||||
 | 
					        if d > 0:
 | 
				
			||||||
 | 
					            return f"{d}d {h}h {m}m"
 | 
				
			||||||
 | 
					        if h > 0:
 | 
				
			||||||
 | 
					            return f"{h}h {m}m"
 | 
				
			||||||
 | 
					        return f"{m}m"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # ---- individual generators (originals) ----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_members_count(self, guild: discord.Guild) -> str:
 | 
				
			||||||
 | 
					        count = sum(1 for m in guild.members if not m.bot)
 | 
				
			||||||
 | 
					        return f"{count} server members"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_pirates_count(self, guild: discord.Guild) -> str:
 | 
				
			||||||
 | 
					        dm = getattr(self.bot, "data_manager", None)
 | 
				
			||||||
 | 
					        n = len(dm.get('pirates')) if dm else 0
 | 
				
			||||||
 | 
					        return f"{n} pirates reported"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_encounters_count(self, guild: discord.Guild) -> str:
 | 
				
			||||||
 | 
					        dm = getattr(self.bot, "data_manager", None)
 | 
				
			||||||
 | 
					        n = len(dm.get('encounters')) if dm else 0
 | 
				
			||||||
 | 
					        return f"{n} reported encounters"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_random_shoutout(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        if not self.full_access_role_id:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        role = guild.get_role(self.full_access_role_id)
 | 
				
			||||||
 | 
					        if not role:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        candidates = [m for m in role.members if not m.bot]
 | 
				
			||||||
 | 
					        if not candidates:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        member = random.choice(candidates)
 | 
				
			||||||
 | 
					        em = random.choice(EMOTES)
 | 
				
			||||||
 | 
					        name = member.display_name
 | 
				
			||||||
 | 
					        return f"{em} {name}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_initiated_count(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        if not self.full_access_role_id:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        role = guild.get_role(self.full_access_role_id)
 | 
				
			||||||
 | 
					        if not role:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        n = sum(1 for m in role.members if not m.bot)
 | 
				
			||||||
 | 
					        return f"{n} initiated members"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_random_phrase(self, guild: discord.Guild) -> str:
 | 
				
			||||||
 | 
					        return random.choice(DUNE_PHRASES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_random_fun_fact(self, guild: discord.Guild) -> str:
 | 
				
			||||||
 | 
					        idx = random.randrange(len(FUN_FACTS))  # 0-based
 | 
				
			||||||
 | 
					        num = idx + 1
 | 
				
			||||||
 | 
					        fact = FUN_FACTS[idx]
 | 
				
			||||||
 | 
					        return f"Fun fact #{num} ->\n{fact}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_top_threat(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        dm = getattr(self.bot, "data_manager", None)
 | 
				
			||||||
 | 
					        pirates = dm.get('pirates') if dm else []
 | 
				
			||||||
 | 
					        if not pirates:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        # sort by threat_level desc, then encounter_count desc, then newest added
 | 
				
			||||||
 | 
					        pirates_sorted = sorted(
 | 
				
			||||||
 | 
					            pirates,
 | 
				
			||||||
 | 
					            key=lambda p: (
 | 
				
			||||||
 | 
					                int(p.get('threat_level', 0)),
 | 
				
			||||||
 | 
					                int(p.get('encounter_count', 0)),
 | 
				
			||||||
 | 
					                float(p.get('added_ts', 0.0))
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            reverse=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        top = pirates_sorted[0]
 | 
				
			||||||
 | 
					        tl = int(top.get('threat_level', 0))
 | 
				
			||||||
 | 
					        name = top.get('character_name') or top.get('account_name') or "Unknown"
 | 
				
			||||||
 | 
					        return f"Top threat: {name} ({tl}%)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_latest_report(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        dm = getattr(self.bot, "data_manager", None)
 | 
				
			||||||
 | 
					        reports = dm.get('reports') if dm else []
 | 
				
			||||||
 | 
					        if not reports:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        latest = max(reports, key=lambda r: float(r.get('ts', 0.0)))
 | 
				
			||||||
 | 
					        char = latest.get('character_name') or "Unknown"
 | 
				
			||||||
 | 
					        acct = latest.get('account_name') or ""
 | 
				
			||||||
 | 
					        # keep brief
 | 
				
			||||||
 | 
					        return f"Latest report: {char}" + (f" ({acct})" if acct else "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_encounters_last_24h(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        dm = getattr(self.bot, "data_manager", None)
 | 
				
			||||||
 | 
					        encs = dm.get('encounters') if dm else []
 | 
				
			||||||
 | 
					        if not encs:
 | 
				
			||||||
 | 
					            return "0 encounters last 24h"
 | 
				
			||||||
 | 
					        since = time.time() - 86400
 | 
				
			||||||
 | 
					        n = sum(1 for e in encs if float(e.get('timestamp', 0.0)) >= since)
 | 
				
			||||||
 | 
					        return f"{n} encounters last 24h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_uptime(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        dm = getattr(self.bot, "data_manager", None)
 | 
				
			||||||
 | 
					        st = (dm.get('boot_state') or [{}])[-1] if (dm and dm.get('boot_state')) else {}
 | 
				
			||||||
 | 
					        boot_ts = float(st.get('last_boot_ts', 0.0))
 | 
				
			||||||
 | 
					        if boot_ts <= 0:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        dur = self._fmt_duration(time.time() - boot_ts)
 | 
				
			||||||
 | 
					        return f"Uptime {dur}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _gen_running_version(self, guild: discord.Guild) -> str | None:
 | 
				
			||||||
 | 
					        dm = getattr(self.bot, "data_manager", None)
 | 
				
			||||||
 | 
					        st = (dm.get('boot_state') or [{}])[-1] if (dm and dm.get('boot_state')) else {}
 | 
				
			||||||
 | 
					        ver = st.get('last_version')
 | 
				
			||||||
 | 
					        if not ver:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        # Occasionally include version (kept as its own generator for randomness)
 | 
				
			||||||
 | 
					        return f"Running {ver}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ============== setup() ==============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def setup(bot: commands.Bot):
 | 
				
			||||||
 | 
					    cog = StatusRotatorCog(bot)
 | 
				
			||||||
 | 
					    await bot.add_cog(cog)
 | 
				
			||||||
 | 
					    print("[status] StatusRotatorCog loaded; rotating presence every",
 | 
				
			||||||
 | 
					          max(1, cfg(bot).int('status_interval_min', 5)), "minute(s).")
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user