Last active 1723284561

Revision 846b1e297efc2358f23cf65e35d67481caf2052c

bot.py Raw
1from interactions import slash_command, SlashContext, Client, slash_option, OptionType, auto_defer, Embed, check, Task, DateTrigger, listen
2from requests import get, post
3from datetime import datetime, timedelta, date
4from re import findall
5from os import environ
6
7bot = Client(token=environ["BOT_TOKEN"])
8
9is_recurring = True
10owner_username = "alyssile"
11officer_role_suffix = "Officer"
12# add IDs for guilds you want the bot to register commands in
13guilds = [353071720342487040, 1038346577267392582]
14role_housel = 1149914848428294315
15role_weekly = 1149914607822061599
16role_tribal = 1149914662197010522
17role_grandc = 1149914810486632488
18role_jumboc = 1149929534129459290
19role_fashio = 1212403886263115786
20# role_testing = 873495029883236392 # TODO
21
22async 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
37async 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()
123async 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
130bot.start()
docker-compose.yml Raw
1services:
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