aly revised this gist . Go to revision
2 files changed, 139 insertions
bot.py(file created)
| @@ -0,0 +1,130 @@ | |||
| 1 | + | from interactions import slash_command, SlashContext, Client, slash_option, OptionType, auto_defer, Embed, check, Task, DateTrigger, listen | |
| 2 | + | from requests import get, post | |
| 3 | + | from datetime import datetime, timedelta, date | |
| 4 | + | from re import findall | |
| 5 | + | from os import environ | |
| 6 | + | ||
| 7 | + | bot = Client(token=environ["BOT_TOKEN"]) | |
| 8 | + | ||
| 9 | + | is_recurring = True | |
| 10 | + | owner_username = "alyssile" | |
| 11 | + | officer_role_suffix = "Officer" | |
| 12 | + | # add IDs for guilds you want the bot to register commands in | |
| 13 | + | guilds = [353071720342487040, 1038346577267392582] | |
| 14 | + | role_housel = 1149914848428294315 | |
| 15 | + | role_weekly = 1149914607822061599 | |
| 16 | + | role_tribal = 1149914662197010522 | |
| 17 | + | role_grandc = 1149914810486632488 | |
| 18 | + | role_jumboc = 1149929534129459290 | |
| 19 | + | role_fashio = 1212403886263115786 | |
| 20 | + | # role_testing = 873495029883236392 # TODO | |
| 21 | + | ||
| 22 | + | async def check_officer(ctx: SlashContext): | |
| 23 | + | return ctx.author.username == owner_username or any(role.name[-len(officer_role_suffix):] == officer_role_suffix for role in ctx.author.roles) | |
| 24 | + | ||
| 25 | + | #@slash_command(name="xivtimers", description="Show current timers", scopes=guilds) | |
| 26 | + | #@slash_option(name="clear", description="Clears the previous message from the channel", required=False, opt_type=OptionType.BOOLEAN) | |
| 27 | + | #@slash_option(name="recur", description="Sets up the command to refresh the embed automatically each time one of the timers expires", required=False, opt_type=OptionType.BOOLEAN) | |
| 28 | + | #@check(check=check_officer) | |
| 29 | + | #@auto_defer() | |
| 30 | + | #async def xivtimers_cmd(ctx: SlashContext, clear: bool=False, recur: bool=False): | |
| 31 | + | # global is_recurring | |
| 32 | + | # if is_recurring: | |
| 33 | + | # await ctx.send(f"There's already a recurring channel update task running... check with {owner_username}", delete_after=10, ephemeral=True) | |
| 34 | + | # return | |
| 35 | + | # await xivtimers(ctx.channel_id, clear, recur) | |
| 36 | + | ||
| 37 | + | async def xivtimers(channel_id, clear: bool=False, recur: bool=False, mention: str=""): | |
| 38 | + | global is_recurring | |
| 39 | + | channel = bot.get_channel(channel_id) | |
| 40 | + | now = datetime.now() | |
| 41 | + | data = get("https://www.xenoveritas.org/static/ffxiv/timers.json").json() | |
| 42 | + | embed = Embed(title="Timers", color="#eb144c") | |
| 43 | + | timers = {} | |
| 44 | + | maintenance = False | |
| 45 | + | #print('data["timers"]', data["timers"]) | |
| 46 | + | for timer in data["timers"]: | |
| 47 | + | if not any(label in timer for label in ["start", "end", "end_label"]): | |
| 48 | + | print(timer) | |
| 49 | + | continue | |
| 50 | + | if timer.get('end',0) and (now-timedelta(hours=6)).timestamp() > int(timer.get('end',0)/1000): | |
| 51 | + | continue | |
| 52 | + | end_label = timer.get("endLabel", f"<t:{int(timer.get('end',0)/1000)}:R>") | |
| 53 | + | ts = datetime.fromtimestamp(int(timer.get('start',0)/1000)) | |
| 54 | + | if "<a href=" in timer["name"]: | |
| 55 | + | name = timer["name"].split('">')[1].split("<")[0] | |
| 56 | + | else: | |
| 57 | + | name = timer["name"] | |
| 58 | + | if "all worlds maintenance" in name.lower() and now > ts and now < datetime.fromtimestamp(int(timer.get('end',0)/1000)): | |
| 59 | + | maintenance = True | |
| 60 | + | timers["Maintenance complete"] = datetime.fromtimestamp(int(timer.get('end',0)/1000)) | |
| 61 | + | if now < ts: | |
| 62 | + | timers[name] = ts | |
| 63 | + | embed.add_field(name=name, value=f"Start{'s' if now.timestamp() < int(timer.get('start',0)/1000) else 'ed'} <t:{int(timer.get('start',0)/1000)}:R>, {'runs' if 'endLabel' in timer else 'ends'} {end_label}") | |
| 64 | + | ||
| 65 | + | if maintenance: | |
| 66 | + | embed.title = "Timers (Maintenance)" | |
| 67 | + | ||
| 68 | + | def generate_field(name: str, hour: int, role: int=None, day: int=None): | |
| 69 | + | nonlocal timers, now | |
| 70 | + | if (day is not None and now.weekday() == day) and now.hour >= hour: | |
| 71 | + | _now = now + timedelta(1) | |
| 72 | + | elif now.hour >= hour: | |
| 73 | + | _now = now + timedelta(1) | |
| 74 | + | else: | |
| 75 | + | _now = now | |
| 76 | + | reset = datetime.combine(_now, datetime.min.time())+timedelta(hours=hour) | |
| 77 | + | if day is not None: | |
| 78 | + | reset += timedelta((day-_now.weekday())%7) | |
| 79 | + | timers[f"<@&{role}>"] = reset | |
| 80 | + | return {"name": name, "value": f"<t:{int(reset.timestamp())}:R>", "inline": True} | |
| 81 | + | ||
| 82 | + | # embed.add_field(name="Patch 6.5", value=f"<t:{int(datetime(2023,10,3,19,00).timestamp())}:R>") | |
| 83 | + | ||
| 84 | + | if not maintenance: | |
| 85 | + | housing_start_date = datetime(2022,5,27,1,0) | |
| 86 | + | if now.hour >= 1: | |
| 87 | + | _now = now+timedelta(1) | |
| 88 | + | else: | |
| 89 | + | _now = now | |
| 90 | + | mod = (_now - housing_start_date).days % 9 | |
| 91 | + | house_reset = datetime.combine(_now, datetime.min.time())+timedelta(abs(mod-5 if mod <= 5 else mod-9))+timedelta(hours=1) | |
| 92 | + | timers[f"<@&{role_housel}>"] = house_reset | |
| 93 | + | embed.add_field(name="Housing " + ("Results ending" if mod > 5 else "Lottery ending"), value=f"<t:{int(house_reset.timestamp())}:R>", inline=True) | |
| 94 | + | ||
| 95 | + | embed.add_field(**generate_field("Weekly Reset", 19, day=1, role=role_weekly)) | |
| 96 | + | embed.add_field(**generate_field("Fashion Report Judging", 19, day=4, role=role_fashio)) | |
| 97 | + | embed.add_field(**generate_field("Tribal/Duty Reset", 1, role=role_tribal)) | |
| 98 | + | embed.add_field(**generate_field("Grand Company Reset", 6, role=role_grandc)) | |
| 99 | + | embed.add_field(**generate_field("Jumbo Cactpot", 19, day=5, role=role_jumboc)) | |
| 100 | + | ||
| 101 | + | #print("\n".join(f"{t:%Y/%m/%d %H:%M:%S}" for t in timers.values())) | |
| 102 | + | ||
| 103 | + | async for message in channel.history(limit=2 if not is_recurring else 1): | |
| 104 | + | print(message) | |
| 105 | + | if message.author == bot.user.id: | |
| 106 | + | await message.delete() | |
| 107 | + | ||
| 108 | + | # timers[f"<@&{role_testing}>"] = datetime.now()+timedelta(seconds=20) # TODO | |
| 109 | + | ||
| 110 | + | _mention = mention | |
| 111 | + | mention, earliest = min(timers.items(), key=lambda x: x[1]) | |
| 112 | + | embed.add_field(name="Refreshing", value=f"<t:{int(earliest.timestamp())}:R>", inline=True) | |
| 113 | + | ||
| 114 | + | await channel.send(content=_mention, embed=embed) | |
| 115 | + | ||
| 116 | + | print(f"Setting up timer at {earliest:%Y/%m/%d %H:%M:%S} in {channel_id}") | |
| 117 | + | @Task.create(DateTrigger(earliest)) | |
| 118 | + | async def recurring(): | |
| 119 | + | await xivtimers(channel_id, clear, recur, mention=mention) | |
| 120 | + | recurring.start() | |
| 121 | + | ||
| 122 | + | @listen() | |
| 123 | + | async def on_startup(): | |
| 124 | + | print("Bot started") | |
| 125 | + | await xivtimers(1063529561486270494, True, True) | |
| 126 | + | await (bot.get_channel("1127730623818256384")).send("xivtimers restarted") | |
| 127 | + | # channel = bot.get_channel("1127730623818256384") | |
| 128 | + | # await channel.send("Bot restarted, please run `/xivtimers clear:True recur:True` in <#1063529561486270494> (<@133057442425602048>)") | |
| 129 | + | ||
| 130 | + | bot.start() | |
docker-compose.yml(file created)
| @@ -0,0 +1,9 @@ | |||
| 1 | + | services: | |
| 2 | + | bot: | |
| 3 | + | image: ghcr.io/alyssadev/python-discord-bot:latest | |
| 4 | + | environment: | |
| 5 | + | - BOT_TOKEN= | |
| 6 | + | volumes: | |
| 7 | + | - .:/usr/src/app | |
| 8 | + | - /etc/localtime:/etc/localtime:ro | |
| 9 | + | restart: always | |
Newer
Older