from idlelib import autocomplete from random import choices import discord from discord.ext import commands, tasks from discord.commands import option import requests import json from datetime import datetime import aiohttp import os import asyncio import ast from math import floor ### reads token files def readToken(filePath): with open(filePath, 'r') as f: return f.readline() ### sends a get request to the sheet async def getFromSheet(gsheetToken, data): params = {"type":data} #print(params) async with session.get(gsheetToken, params=params) as response: #print("Status:", response.status) text = await response.text() #print(text) return text ### takes a json, returns a http code async def postToSheet(data, gsheetToken): async with session.post(gsheetToken, json=data) as response: text = await response.text() #print(text) #print("Status:", response.status) return response.status ### carrier state parser, takes the values from the sheet and turn it into something we can use async def parseCarrierState(state): tupleList = [] splitState = state.split(",") for i in range(0,len(splitState),2): carrier = splitState[i] state = splitState[i+1] if carrier != "" and state != "": tupleList.append((carrier, state)) return tupleList ### converts a list of tuple back to the sheet syntax (CSV) async def toRawState(tupleList): rawState = [] for value in tupleList: for subval in value: rawState.append([subval]) rawLen = len(rawState) for i in range(rawLen, 41): rawState.append([""]) return rawState async def getListOfCarriers(state): splitState = state.split(",") carrierList = [] for i in range(0,len(splitState),2): carrier = splitState[i] if carrier != "": carrierList.append(carrier) return carrierList ### used to query the carrier state internally. Why did I make this???? async def getInternalCarrierState(): response = await getFromSheet(gsheetToken, "carrier") return response async def getCarriers(ctx: discord.AutocompleteContext): state = await getInternalCarrierState() carrierList = await getListOfCarriers(state) # print(carrierList) # return [carrier for carriers in carrierList] return carrierList async def getCommodities(ctx: discord.AutocompleteContext): with open("commodity.needed","r") as f: listOfCommodities = ast.literal_eval(f.readline()) return listOfCommodities ###creates a nice progress bar from two values async def createProgressBar(a,b): #print(a, b) ratio = floor(((b-a)/b) * 10) #print(ratio) progressBar = f"{'◼'* ratio}{'◻'*(10-ratio)} {ratio*10}%" return progressBar #creates the file if it doesn't exist. stores last command used by... users on this delivery. async def writeDeliveryToCommandFile(author, command): #shitty hack to see if the file is empty path = os.getcwd() + "/" + lastCommandFile empty = os.stat(path).st_size < 1 with open(lastCommandFile, 'r') as f: if empty: jsonContent = json.loads("{}") else: jsonContent = json.load(f) with open(lastCommandFile, 'w') as f: jsonContent[author] = command json.dump(jsonContent, f) async def getDataFromDeliveryFile(author): path = os.getcwd() + "/" + lastCommandFile empty = os.stat(path).st_size < 1 with open(lastCommandFile, 'r') as f: if empty: return None else: jsonContent = json.load(f) if author in jsonContent.keys(): return jsonContent[author] else: return None async def delLastCommandFile(): open(lastCommandFile, 'w').close() bot = commands.Bot() # various tokens and global vars discordToken = readToken("discord.token") gsheetToken = readToken("sheet.token") lastCommandFile = "command.last" print("hey wassup") #there's a small chance the files don't exist. If they don't we create an empty file. try: with open("station.name", 'r') as f: stationName = f.readline() except FileNotFoundError: open("station.name", 'w').close() stationName = "REPLACEME" try: with open("station.url",'r') as f: stationUrl = f.readline() except FileNotFoundError: open("station.url", 'w').close() stationUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" # default command to see if the bot has lived @bot.listen() async def on_connect(): global session print("I'm alive, bitch") session = aiohttp.ClientSession() @bot.event async def on_ready(): writeToWhatsLeftToHaul.start() writeTocarrierState.start() print("I'm ready, bitch") # delivery command # guild IDs are both IDA servers @bot.slash_command( name="delivery", # guild_ids=[401372086746087425], description= "sends your delivery to the bot!" ) @option("commodity",required=True, description="The Commodity you want to deliver", autocomplete=getCommodities) @option("quantity",required=True, description="Please be nice and input a value between 1 and 1326 (new rack update!!!!1!1)", type=int, min_value=1, max_value=1326) @option("target", required=True, description="Are you delivering to a carrier or the target station?", type=str, choices=['Station', 'Carrier']) async def delivery(ctx,commodity, quantity,target): author = str(ctx.author) author = author[:author.find(" ")] await ctx.defer() data = { "postType":"delivery", "username":author, "commodity": commodity, "quantity": quantity, "target": target } try: response = await postToSheet(data, gsheetToken) if response == 200: await ctx.followup.send(f"your delivery of {quantity} of {commodity} to a {target} has been added to the sheet!") print(f"[{datetime.isoformat(datetime.now())}]{author} delivery of {quantity} of {commodity} to a {target}") await writeDeliveryToCommandFile(author, data) else: await ctx.followup.send(f"Failed to log delivery (HTTP {response}). Please contact the yellow people if that keeps happening") except Exception as e: await ctx.followup.send(f"help : Error: {e}") # delivery command for misc commodity only # guild IDs are both IDA servers @bot.slash_command( name="misc", # guild_ids=[401372086746087425], description= "Used for the commodities we're too lazy to create a sheet for" ) async def misc(ctx, quantity: discord.Option(discord.SlashCommandOptionType.integer, description="Please be nice and input a value between 1 and 1326 (new rack update!!!!1!1)", min_value=1, max_value=1326), target: discord.Option(str, choices=['Station', 'Carrier']) ): author = str(ctx.author) author = author[:author.find(" ")] await ctx.defer() data = { "postType":"delivery", "username":author, "commodity": "Misc", "quantity": quantity, "target": target } try: response = await postToSheet(data, gsheetToken) if response == 200: await ctx.followup.send(f"your delivery of {quantity} of Miscellaneous stuff to a {target} has been added to the sheet!") print(f"[{datetime.isoformat(datetime.now())}]{author} delivery of {quantity} of Miscellaneous to a {target}") await writeDeliveryToCommandFile(author, data) else: await ctx.followup.send(f"Failed to log delivery (HTTP {response}). Please contact the yellow people if that keeps happening") except Exception as e: await ctx.followup.send(f"help : Error: {e}") @bot.slash_command( name="last", # guild_ids=[401372086746087425], description= "send the last delivery you made again!" ) async def last(ctx): author = str(ctx.author) author = author[:author.find(" ")] data = await getDataFromDeliveryFile(author) await ctx.defer() if data is not None: try: response = await postToSheet(data, gsheetToken) if response == 200: await ctx.followup.send(f"your delivery of {data['quantity']} of {data['commodity']} to a {data['target']} has been added to the sheet!") print(f"[{datetime.isoformat(datetime.now())}] delivery of {data['quantity']} of {data['commodity']} to a {data['target']}") await writeDeliveryToCommandFile(author, data) else: await ctx.followup.send(f"Failed to log delivery (HTTP {response}). Please contact the yellow people if that keeps happening") except Exception as e: await ctx.followup.send(f"help : Error: {e}") else: await ctx.followup.send(f"You haven't delivered to this build yet, or I have amnesia.") #change url command @bot.slash_command( name="change-sheet-url", # guild_ids=[401372086746087425], description= "changes the URL of the active sheet, use with caution" ) async def changeSheetUrl(ctx, url:discord.Option(discord.SlashCommandOptionType.string, description="the new URL") ): global gsheetToken global stationName global stationURL await ctx.defer() try: with open("sheet.token",'w') as f: f.write(url) await ctx.followup.send(f"URL has been set to {url}") gsheetToken = readToken("sheet.token") await delLastCommandFile() print("deleted command.last file content") # gets the name and URL from the sheet, then store it in a file so we don't ask the sheet again. name, url = (await getFromSheet(gsheetToken, "sheet")).split("|") with open("station.name", 'w') as f: f.write(name) stationName = name with open("station.url", 'w') as f: f.write(url) stationURL = url writeToWhatsLeftToHaul.restart() except Exception as e: await ctx.followup.send(f"something shat the bed") @bot.slash_command( name="setcarrierstate", # guild_ids=[401372086746087425], description= "Sets a carrier state on the sheet" ) @option("carrier", description="The carrier you want to edit", autocomplete=getCarriers) @option("state",required=True, description="The carrier you want to edit", choices=["Loading","Unloading","Full","Empty","Inactive"]) async def setCarrierState(ctx, carrier, state): response = await getFromSheet(gsheetToken, "carrier") carrierState = await parseCarrierState(response) author = str(ctx.author) newCarrierState = [] #print(f"requested values: {carrier} to {state}") #print(f"current values : {carrierState}") for carrierS, stateS in carrierState: if carrier == carrierS: newCarrierState.append((carrier,state)) else: newCarrierState.append((carrierS,stateS)) #print(f"new values : {newCarrierState}") rawState = await toRawState(newCarrierState) #print(rawState) await ctx.defer() i = 1 text = "Current Carrier State : \n" data = { "postType":"carrier", "carrierState": rawState } try: response = await postToSheet(data, gsheetToken) if response == 200: await ctx.followup.send(f"The carrier {carrier} has been set to {state}") print(f"{author} set {carrier} state to {state}") else: await ctx.followup.send(f"i fucked up ") except Exception as e: await ctx.followup.send(f"help : Error: {e}") #writes to the whats left to haul channel ever 60 seconds about.. the stuff left to haul. #also updates the current commodity List @tasks.loop(seconds=60) async def writeToWhatsLeftToHaul(): response = await getFromSheet(gsheetToken, "commodity") commodity, values, initial = response.split("|") commodity = commodity.split(",") values = values.split(",") initial = initial.split(",") values = [int(i) for i in values] initial = [int (i) for i in initial] finalDict = {} progressDict = {} neededCommodity = [] for i in range(len(commodity)): finalDict[commodity[i]] = values[i] if initial[i] > 0: progressDict[commodity[i]] = await createProgressBar(values[i],initial[i]) neededCommodity.append(commodity[i]) else: progressDict[commodity[i]] = await createProgressBar(1,1) #finalDict = dict(sorted(finalDict.items(), key=lambda item: item[1], reverse=True)) with open("commodity.needed",'w') as f: f.write(str(neededCommodity)) print("values set") channel = bot.get_channel(1519457137234022460) # await channel.purge(limit=1) embed = discord.Embed(title="Estimated remaining amounts", colour=discord.Colour(0xffdd55), timestamp=datetime.now()) embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/536647643367997492.png") embed2 = discord.Embed(title="Current progress", colour=discord.Colour(0x002f80), timestamp=datetime.now()) embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/536647643367997492.png") for key in finalDict: if finalDict[key] > 0: embed.add_field(name=key, value=finalDict[key], inline=True) embed2.add_field(name=key, value=str(progressDict[key]), inline=True) embed2.set_footer(text="This message updates every 60 seconds(ish). Last updated") embed.set_author(name=u'\U0001F517 ' + stationName + u' \U0001F517', url=stationUrl) embed2.set_author(name=u'\U0001F517 ' + stationName + u' \U0001F517', url=stationUrl) channel = bot.get_channel(1519457137234022460) found = False async for oldmsg in channel.history(limit=2): if oldmsg.author == bot.user: found = True await oldmsg.edit(embeds=[embed, embed2]) break if not found: await channel.send(embeds=[embed, embed2]) #writes the carriers state to... the carrier state channel. Seriously, who is writing this? @tasks.loop(seconds=60) async def writeTocarrierState(): response = await getInternalCarrierState() carrierState = await parseCarrierState(response) channel = bot.get_channel(1521596285981819101) embed = discord.Embed(title="Carriers State", colour=discord.Colour(0x29ac08), timestamp=datetime.now()) embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/536647643367997492.png") embed.set_author(name=u'\U0001F517 ' + stationName + u' \U0001F517', url=stationUrl) embed.set_footer(text="This message updates every 60 seconds(ish). Last updated") for carrier, state in carrierState: embed.add_field(name=carrier, value=state, inline=True) found = False async for oldmsg in channel.history(limit=2): if oldmsg.author == bot.user: found = True await oldmsg.edit(embed=embed) break if not found: await channel.send(embed=embed) #run the damn thing bot.run(discordToken)