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 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 ###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" #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(): print("I'm ready, bitch") writeToWhatsLeftToHaul.start() writeTocarrierState.start() # delivery command # guild IDs are both IDA servers @bot.slash_command( name="delivery", # guild_ids=[401372086746087425], description= "sends your delivery to the bot!" ) async def delivery(ctx, commodity:discord.Option(str, choices=[ "Aluminium", "Ceramic Composites", "CMM Composite", "Computer Components", "Copper", "Food Cartridges", "Fruit and Vegetables", "Insulating Membrane", "Liquid Oxygen", "Medical Diagnostic Equipment", "Non-Lethal Weapons", "Polymers", "Power Generators", "Semiconductors", "Steel", "Superconductors", "Titanium", "Water", "Water Purifiers" ]), 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": 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. @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 = {} for i in range(len(commodity)): finalDict[commodity[i]] = values[i] if initial[i] > 0: progressDict[commodity[i]] = await createProgressBar(values[i],initial[i]) else: progressDict[commodity[i]] = await createProgressBar(1,1) #finalDict = dict(sorted(finalDict.items(), key=lambda item: item[1], reverse=True)) 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)