426 lines
21 KiB
Python
426 lines
21 KiB
Python
import disnake
|
||
from disnake.ext import commands
|
||
import json
|
||
import modals
|
||
import helpers
|
||
import time
|
||
import embed_factory
|
||
from disnake.ext import tasks
|
||
import sqlite3
|
||
from sqlite3 import Cursor, Error
|
||
import asyncio
|
||
import botOptions
|
||
import bingo
|
||
import random
|
||
import math
|
||
import os
|
||
from datetime import datetime,timezone
|
||
import datetime
|
||
import sbclasses
|
||
import requests
|
||
|
||
intents = disnake.Intents().all()
|
||
|
||
bot = commands.Bot(
|
||
command_preifx='%',
|
||
sync_commands_debug=False,
|
||
intents=intents
|
||
)
|
||
|
||
|
||
@bot.event
|
||
async def on_ready():
|
||
print('Logged In')
|
||
|
||
@bot.slash_command(description='List some villages in need of LL')
|
||
async def ll(inter:disnake.ApplicationCommandInteraction):
|
||
playerCP = helpers.sql_get("SELECT cp,prefPikes,prefArchers FROM user WHERE dID = {dID}".format(dID=inter.author.id))
|
||
print(playerCP)
|
||
cp = 0
|
||
prefArcher = 300
|
||
prefPikes = 200
|
||
if len(playerCP):
|
||
cp = int(playerCP[0][0])
|
||
if playerCP[0][1] != None:
|
||
prefArcher = int(playerCP[0][2])
|
||
prefPikes = int(playerCP[0][1])
|
||
await inter.response.send_modal(modal=modals.LLModal(cp=cp,pike=prefPikes,archer=prefArcher))
|
||
|
||
@bot.event
|
||
async def on_guild_available(guild):
|
||
helpers.sql_set('INSERT INTO guild(gID,name) VALUES(?,?) ON CONFLICT DO NOTHING',(guild.id,guild.name))
|
||
|
||
typeList = ['ll']
|
||
@bot.slash_command(description='set LL advert channel')
|
||
async def create_advert(inter:disnake.ApplicationCommandInteraction,advert_type:str = commands.Param(description='What type=',choices=typeList), target: disnake.abc.GuildChannel = commands.Param(description='Channel override, optional')):
|
||
embed = disnake.Embed(title='Looking for Liege Lords')
|
||
result = json.loads(helpers.sql_get("SELECT lldata FROM guild WHERE gID = {gID}".format(gID = inter.guild.id))[0][0])
|
||
if advert_type not in result:
|
||
message = await target.send(embed=embed)
|
||
await message.create_thread(name="Requests")
|
||
result[advert_type] = (message.channel.id,message.id)
|
||
helpers.sql_set("INSERT INTO guild(gID,lldata) VALUES(?,?) ON CONFLICT DO UPDATE SET lldata = excluded.lldata",(message.guild.id,json.dumps(result)))
|
||
else:
|
||
await inter.response.send_message(content="Already set",ephemeral=True)
|
||
|
||
@bot.slash_command(description="List some info about target")
|
||
async def check_player(inter:disnake.ApplicationCommandInteraction,user:str = commands.Param(description='Player Name')):
|
||
embed = embed_factory.check_player(user)
|
||
await inter.response.send_message(embed=embed)
|
||
|
||
|
||
@bot.event
|
||
async def on_message_interaction(inter:disnake.MessageInteraction):
|
||
if inter.component.custom_id == 'llcheck' and not inter.response.is_done():
|
||
await inter.response.defer(ephemeral=True)
|
||
inter.component.disabled = True
|
||
ll_data_dict = json.loads(helpers.sql_get("SELECT lldata FROM guild WHERE gID = {gid}".format(gid=inter.guild.id))[0][0])
|
||
ll_channel = inter.guild.get_channel_or_thread(ll_data_dict['ll'][0])
|
||
ll_thread_message = await ll_channel.fetch_message(ll_data_dict['ll'][1])
|
||
helpers.sql_set("UPDATE llrequests SET fulfilled = ? WHERE messageID = ?",(time.time(),inter.message.id))
|
||
await ll_thread_message.edit(embed= embed_factory.lfll(ll_data_dict['ll'][1]))
|
||
|
||
message = await inter.message.delete()
|
||
|
||
|
||
return
|
||
|
||
|
||
|
||
|
||
timedAttacks = {}
|
||
timesMessages = {}
|
||
|
||
|
||
def sql_connection():
|
||
try:
|
||
|
||
con = sqlite3.connect('sbingo2.sqlite')
|
||
|
||
return con
|
||
|
||
except Error:
|
||
|
||
print(Error)
|
||
|
||
def sql_setSeed(con,entry):
|
||
cursorObj = con.cursor()
|
||
cursorObj.execute('INSERT INTO overrides(dID,seed) VALUES(?,?) ON CONFLICT(dID) DO UPDATE SET seed = excluded.seed',entry)
|
||
cursorObj.close()
|
||
con.commit()
|
||
|
||
def sql_setCard(con,entry):
|
||
cursorObj = con.cursor()
|
||
cursorObj.execute('INSERT INTO defaultCard(dID,speed) VALUES(?,?) ON CONFLICT(dID) DO UPDATE SET speed = excluded.speed',entry)
|
||
cursorObj.close()
|
||
con.commit()
|
||
|
||
def sql_getCard(con,id):
|
||
cursorObj = con.cursor()
|
||
query = 'SELECT speed FROM defaultCard WHERE dID = '+str(id)
|
||
cursorObj.execute(query)
|
||
result = cursorObj.fetchall()
|
||
cursorObj.close()
|
||
if len(result) == 1:
|
||
return result[0][0]
|
||
return result
|
||
|
||
def sql_addVM(con,name,dateTime):
|
||
ok = True
|
||
data = (name.lower(),dateTime)
|
||
cursorObj = con.cursor()
|
||
try:
|
||
cursorObj.execute('INSERT INTO vecations(name,date) VALUES(?,?)',data)
|
||
except Error:
|
||
ok = False
|
||
cursorObj.close()
|
||
con.commit()
|
||
return ok
|
||
|
||
def sql_getVM(con):
|
||
cursorObj = con.cursor()
|
||
cursorObj.execute('SELECT * FROM vecations where date > unixepoch("now","-14 days");')
|
||
result = cursorObj.fetchall()
|
||
cursorObj.close()
|
||
return result
|
||
|
||
def sql_getSeed(con,id):
|
||
cursorObj = con.cursor()
|
||
query = 'SELECT dID, seed FROM overrides WHERE dID = '+str(id)
|
||
cursorObj.execute(query)
|
||
result = cursorObj.fetchall()
|
||
cursorObj.close()
|
||
return result
|
||
|
||
def generateBingoCard(display_name, id):
|
||
bingoimg = bingo.generate_bingo(display_name,id)
|
||
bingoimg.save(display_name+'.png')
|
||
|
||
@bot.event
|
||
async def on_message(message):
|
||
if message.author == bot.user:
|
||
return
|
||
|
||
if message.content.startswith(botOptions.prefix):
|
||
#Potential command
|
||
command = message.content.lower().split()[0][1:] or None
|
||
args = message.content.split()[1:] or [None]
|
||
match command:
|
||
case 'spin':
|
||
random.seed(time.time())
|
||
response = bingo.generate_gif_test(str(message.id),1)
|
||
await message.channel.send(content='Spinning the wheel for: '+message.author.display_name,file=disnake.File(str(message.id)+".gif"))
|
||
if response == 'T2':
|
||
await asyncio.sleep(4)
|
||
response = bingo.generate_gif_test(str(message.id),2)
|
||
await message.channel.send(content='LUCKY! You won another spin!',file=disnake.File(str(message.id)+".gif"))
|
||
if response == 'T3':
|
||
await asyncio.sleep(4)
|
||
response = bingo.generate_gif_test(str(message.id),3)
|
||
await message.channel.send(content='SPINTASTIC!!! Max level spin achieved',file=disnake.File(str(message.id)+".gif"))
|
||
await asyncio.sleep(4)
|
||
await message.channel.send('Congratulations! You won: '+response)
|
||
os.remove(str(message.id)+".gif")
|
||
case 'bingo':
|
||
seed = sql_getSeed(con,message.author.id)
|
||
if seed:
|
||
generateBingoCard(message.author.display_name,seed[0][1])
|
||
else:
|
||
generateBingoCard(message.author.display_name,message.author.id)
|
||
await message.channel.send(content='Here is your bingo card, if you find something impossible ask for an override!',file=disnake.File(message.author.display_name+".png"))
|
||
os.remove(message.author.display_name+".png")
|
||
case 'reroll':
|
||
if message.author.id in botOptions.verifiedUsers:
|
||
if len(args) != 1:
|
||
await message.channel.send('Expecting an user ID')
|
||
elif not args[0].isnumeric():
|
||
await message.channel.send('The argument needs to be the user ID')
|
||
else:
|
||
target = message.guild.get_member(int(args[0]))
|
||
print(args)
|
||
print(message.guild)
|
||
print(target)
|
||
if target == None:
|
||
await message.channel.send("Can't find the user in this disnake")
|
||
await message.delete()
|
||
return
|
||
newRandom = math.floor(random.random()*100000000000)
|
||
sql_setSeed(con,(target.id,newRandom))
|
||
generateBingoCard(target.display_name,newRandom)
|
||
await message.channel.send('New seed set!',file=disnake.File(target.display_name+".png"))
|
||
os.remove(target.display_name+".png")
|
||
case 'vm':
|
||
if len(args) < 1 or args[0] == 'list':
|
||
vms = sql_getVM(con)
|
||
print(datetime.fromtimestamp(vms[0][1],timezone.utc))
|
||
vmList = 'Players potentially still on VM:'
|
||
if len(vms) == 0:
|
||
vmList += '\n-None'
|
||
else:
|
||
for player in vms:
|
||
vmList += '\n'+player[0]+' - <t:'+str(int(player[1]))+':R>'
|
||
await message.channel.send(vmList)
|
||
if len(args) == 1:
|
||
if sql_addVM(con,args[0],message.created_at.replace(tzinfo=timezone.utc).timestamp()):
|
||
await message.channel.send(args[0]+' Added to VM list!')
|
||
else:
|
||
await message.channel.send(args[0]+' Is already on the list')
|
||
case 'test':
|
||
editAttack = timedAttacks[args[0]]
|
||
editAttack.setMark(time.time())
|
||
|
||
case ('timed'|'attack'|'countdown'):
|
||
#validate args
|
||
if len(args) <= 1:
|
||
if args[0] == 'help' or args[0] == None:
|
||
embed = disnake.Embed(color=0x5eb4ee,title="<:SB:947837030782615613> Sheriff Timed Attacks for dummies!"\
|
||
,description="The SB Sheriff can keep track of your times and participating villas!\nThis can be used to keep track of targets and work out what to set the timer to,\n but it can also show a countdown for each attack \
|
||
Granted this can be a bit slow, and inconvenient for some, but on large timed operations or keeping track of multiple attacks it can be quite useful!\n\n \
|
||
The only command needed is: **{prefix}timed <target ID> <Time> <Name of Attack>**\n\
|
||
**<target ID>** : The village ID of intended target\n\
|
||
**<Time>** : The time you want to add, must be in HH:MM:SS or MM:SS format. Example 1:23:45\n\
|
||
**<Name of Attack>** : Name of the attacking villa, keep it short and don't use spaces. Example: Mayo5\n\n\
|
||
Command Example: {prefix}timed 12345 1:23:45 Ros1\n\n\
|
||
You can add as many or few attacks needed, the Target ID and the time to set timers to is displayed in the attack event.\n\
|
||
the <:timer_green:947634559225327707> reaction can be used by anyone participating to convert the times to countdowns. After clicking you have 10 seconds to send the longest attack".format(prefix=botOptions.prefix))
|
||
embed.set_footer(text="Command aliases: timed,attack,countdown")
|
||
await message.channel.send(embed=embed)
|
||
await message.delete()
|
||
return
|
||
|
||
if len(args) < 3:
|
||
#Not the correct number of args
|
||
await message.channel.send(delete_after=30.0 ,content="Invalid arguments! Usage: {prefix}{command} targetID timeToLand shortName\nExample: {prefix}{command} 69420 1:43:25 Mayo4\nYour input: {msg}".format(msg=message.content,prefix=botOptions.prefix,command=command))
|
||
await message.delete()
|
||
return
|
||
if not ':' in args[1]:
|
||
await message.channel.send(delete_after=30.0 ,content="Invalid timestamp, HH:MM:SS or MM:SS")
|
||
await message.delete()
|
||
return
|
||
tempTime = [int(n) for n in args[1].split(':')]
|
||
if len(tempTime) > 3 or len(tempTime) < 2:
|
||
await message.channel.send(delete_after=30.0 ,content="Invalid timestamp, HH:MM:SS or MM:SS")
|
||
await message.delete()
|
||
return
|
||
#Sanity checks passed, create the event
|
||
if len(tempTime) == 3:
|
||
attacktime = (tempTime[0]*3600)+(tempTime[1]*60)+tempTime[2]
|
||
else:
|
||
attacktime = (tempTime[0]*60)+tempTime[1]
|
||
#Determine if this is a new attack:
|
||
|
||
|
||
if args[0] in timedAttacks:
|
||
editAttack = timedAttacks[args[0]]
|
||
editAttack.addAttack(message.author.display_name,attacktime,args[2])
|
||
messageEmbed = editAttack.generateEmbed()
|
||
#fetch message object
|
||
target_message = await message.guild.get_channel(editAttack.cID).fetch_message(editAttack.mID)
|
||
await target_message.edit(embed = messageEmbed,suppress=False,content=None)
|
||
else:
|
||
attackObject = sbclasses.TimedAttack(message.author.display_name,message.channel.id,args[0],attacktime,args[2])
|
||
attackObject.addAttack(message.author.display_name,attacktime,args[2])
|
||
messageEmbed = attackObject.generateEmbed()
|
||
sendtMessage = await message.channel.send(embed = messageEmbed)
|
||
#print(message.guild.emojis)
|
||
startEmoji = await message.guild.fetch_emoji(947634559225327707)
|
||
await sendtMessage.add_reaction(startEmoji)
|
||
attackObject.setMessageID(sendtMessage.id)
|
||
timedAttacks[args[0]]=attackObject
|
||
if not updateEmbeds.is_running():
|
||
updateEmbeds.start()
|
||
|
||
|
||
case ('timescard'|'defaultcard'):
|
||
if len(args) != 1:
|
||
await message.channel.send(delete_after=30.0 ,content="Must supply a number, (2,3,4,5,6)")
|
||
await message.delete()
|
||
return
|
||
elif len(args) == 1:
|
||
if args[0].isnumeric():
|
||
sql_setCard(con,(message.author.id,int(args[0])))
|
||
|
||
|
||
case ('times'|'calc'|'calculate'|'card'|'time'):
|
||
if len(args) <= 1:
|
||
if args[0] == 'help' or args[0] == None:
|
||
embed = disnake.Embed(color=0x5eb4ee,title="<:SB:947837030782615613> Sheriff Time Converter for dummies!"\
|
||
,description="The SB Sheriff can calculate your times!\n\
|
||
It can accept a single time or as many times as you need, you can then use the numbers below to switch output\n\n \
|
||
The two commands needed is: **{prefix}times <HH:MM:SS>** and **{prefix}timescard <number>** \n\
|
||
Command Example: {prefix}times 1:23:45 23:45 45 23423\nCommand Example: {prefix}timescard 5\nThe timescard command sets your personal default, otherwise its assumed to be 4\n\n\
|
||
To try and limit spam the messages delete themselves after a while!".format(prefix=botOptions.prefix))
|
||
embed.set_footer(text="Command aliases: times,calc,calculate,card,time")
|
||
await message.channel.send(embed=embed)
|
||
await message.delete()
|
||
return
|
||
|
||
timeList = []
|
||
times = sql_getCard(con,message.author.id) or None
|
||
for arg in args:
|
||
if not ":" in arg:
|
||
if not arg.isnumeric():
|
||
await message.channel.send(delete_after=30.0 ,content="Invalid timestamp, HH:MM:SS or MM:SS or Seconds - Offender:{arg} -- Command sendt: {content}".format(arg=arg,content=message.content))
|
||
await message.delete()
|
||
return
|
||
tempTime = [int(n) for n in arg.split(':')]
|
||
for n in range(0,3-len(tempTime)):
|
||
tempTime = [0]+tempTime
|
||
timeList.append(tempTime)
|
||
if len(timeList) == 1 and not times:
|
||
attacktime = (timeList[0][0]*3600)+(timeList[0][1]*60)+timeList[0][2]
|
||
await message.channel.send(delete_after=60.0,content="Here's the carded times for: {t1}\nUse {prefix}timescard to change your default card\n**2x:** {t2}\n**3x:** {t3}\n**4x:** {t4}\n**5x:** {t5}\n**6x:** {t6}".format(t1=datetime.timedelta(seconds=attacktime),t2=datetime.timedelta(seconds=int(attacktime/2)),t3=datetime.timedelta(seconds=int(attacktime/3)),t4=datetime.timedelta(seconds=int(attacktime/4)),t5=datetime.timedelta(seconds=int(attacktime/5)),t6=datetime.timedelta(seconds=int(attacktime/6))))
|
||
await message.delete()
|
||
return
|
||
if not times: times = 4
|
||
textContent = "**Modifying times to {times}x:**\nUse {prefix}timescard to change your default card\n".format(times=times,prefix=botOptions.prefix)
|
||
for t in timeList:
|
||
attacktime = (t[0]*3600)+(t[1]*60)+t[2]
|
||
textContent += "{t} -{card}x-> {t1}\n".format(t=datetime.timedelta(seconds=attacktime),t1=datetime.timedelta(seconds=int(attacktime/times)),card=times)
|
||
textContent += "This message will selfdestruct in <t:{t}:R>".format(t=int(time.time())+240)
|
||
newMessage = await message.channel.send(content=textContent, delete_after=240.0)
|
||
timesMessages[newMessage.id] = timeList
|
||
await newMessage.add_reaction("2️⃣")
|
||
await newMessage.add_reaction("3️⃣")
|
||
await newMessage.add_reaction("4️⃣")
|
||
await newMessage.add_reaction("5️⃣")
|
||
await newMessage.add_reaction("6️⃣")
|
||
|
||
|
||
|
||
case _:
|
||
return
|
||
await message.delete()
|
||
|
||
def updateTimes(id,times):
|
||
timeList = timesMessages[id]
|
||
textContent = "**Modifying times to {times}x:**\nUse {prefix}timescard to change your default card\n".format(times=times,prefix=botOptions.prefix)
|
||
for t in timeList:
|
||
attacktime = (t[0]*3600)+(t[1]*60)+t[2]
|
||
textContent += "{t} -{card}x-> {t1}\n".format(t=datetime.timedelta(seconds=attacktime),t1=datetime.timedelta(seconds=int(attacktime/times)),card=times)
|
||
textContent += "This message will selfdestruct in <t:{t}:R>".format(t=int(time.time())+240)
|
||
return textContent
|
||
|
||
@bot.event
|
||
async def on_raw_reaction_add(event):
|
||
if event.user_id == bot.user.id: return
|
||
if event.message_id in timesMessages.keys():
|
||
if event.emoji.name == '2️⃣':
|
||
updateText = updateTimes(event.message_id,2)
|
||
if event.emoji.name == '3️⃣':
|
||
updateText = updateTimes(event.message_id,3)
|
||
if event.emoji.name == '4️⃣':
|
||
updateText = updateTimes(event.message_id,4)
|
||
if event.emoji.name == '5️⃣':
|
||
updateText = updateTimes(event.message_id,5)
|
||
if event.emoji.name == '6️⃣':
|
||
updateText = updateTimes(event.message_id,6)
|
||
targetMessage = await bot.get_channel(event.channel_id).fetch_message(event.message_id)
|
||
await targetMessage.edit(content=updateText,delete_after=240.0)
|
||
if event.emoji.id == 947634559225327707:
|
||
if len(timedAttacks) == 0: return
|
||
reactedAttack = None
|
||
for attack in timedAttacks.values():
|
||
if attack.mID == event.message_id:
|
||
reactedAttack = attack
|
||
if reactedAttack == None: return
|
||
for name in reactedAttack.attackers.keys():
|
||
if name == event.member.display_name:
|
||
if reactedAttack.mark > 0: return
|
||
reactedAttack.setMark(time.time())
|
||
messageEmbed = reactedAttack.generateEmbed()
|
||
#fetch message object
|
||
target_message = await bot.get_channel(reactedAttack.cID).fetch_message(reactedAttack.mID)
|
||
await target_message.edit(embed = messageEmbed,suppress=False,content=None)
|
||
|
||
|
||
|
||
@tasks.loop(seconds=1.0)
|
||
async def updateEmbeds():
|
||
await bot.wait_until_ready()
|
||
if len(timedAttacks) == 0:
|
||
updateEmbeds.stop()
|
||
else:
|
||
toPop = []
|
||
for attack in timedAttacks.values():
|
||
if attack.mark == 0:
|
||
continue
|
||
if attack.needsUpdate():
|
||
embed = attack.generateEmbed()
|
||
if attack.isOver():
|
||
embed = attack.generateEmbed()
|
||
toPop.append(attack.tID)
|
||
print("Updated a embed")
|
||
msg = await bot.get_channel(attack.cID).fetch_message(attack.mID)
|
||
await msg.edit(embed=embed)
|
||
|
||
if len(toPop):
|
||
for pops in toPop:
|
||
timedAttacks.pop(pops)
|
||
if 0 == len(timedAttacks):
|
||
print("updateEmbeds Hibernating")
|
||
updateEmbeds.stop()
|
||
|
||
con = sql_connection()
|
||
#bot.run('OTk0NzAzNTcxMjMwNjY2ODk1.GFMMJU.PZCwXKPC76haun-uk7ESYcVfODoCNMXs486P9U')
|
||
bot.run('OTUxMjU2NTgxMjU1ODc2Njcw.GW5cRr.De_TGPkljNWJEJJJR8LrmI8FtAZEav9mC69OEk') |