2024-03-26 00:30:05 +00:00
import discord
2024-04-06 03:45:38 +00:00
from discord . ext import commands , tasks
from discord import app_commands
2024-03-26 00:30:05 +00:00
import os
2024-04-02 22:45:34 +00:00
from dotenv import load_dotenv
import sqlite3
2024-03-26 00:30:05 +00:00
import logging
2024-04-02 22:45:34 +00:00
from datetime import datetime , timezone
2024-04-06 03:45:38 +00:00
from discord import ui , Interaction
2024-04-10 22:57:28 +00:00
import random
2024-04-10 22:57:29 +00:00
import sys
2024-04-10 22:57:07 +00:00
import requests
import time
2024-04-06 03:45:38 +00:00
2024-04-10 22:57:07 +00:00
## Defines ##
2024-04-06 03:45:38 +00:00
2024-04-10 22:57:07 +00:00
# Load environment variables from .env file
load_dotenv ( )
2024-04-09 02:17:16 +00:00
2024-04-10 22:57:07 +00:00
# Load bot token and welcome channel id from environment variables
TOKEN = os . getenv ( ' BOT_TOKEN ' )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
# Setup logging
logging . basicConfig ( filename = ' bot.log ' , level = logging . INFO )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
## Function and Class Definitions ##
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
class PersistentViewBot ( commands . Bot ) :
def __init__ ( self ) :
# Define intents
intents = discord . Intents . none ( )
# intents.auto_moderation = True
# intents.auto_moderation_configuration = True
# intents.auto_moderation_execution = True
# intents.bans = True
# intents.dm_messages = True
# intents.dm_reactions = True
# intents.dm_typing = True
# intents.emojis = True
# intents.emojis_and_stickers = True
# intents.guild_messages = True
# intents.guild_reactions = True
# intents.guild_scheduled_events = True
# intents.guild_typing = True
intents . guilds = True
# intents.integrations = True
# intents.invites = True
# intents.members = True
intents . message_content = True
intents . messages = True
# intents.moderation = True
# intents.presences = True
# intents.reactions = True
# intents.typing = True
# intents.value = True
# intents.voice_states = True
# intents.webhooks = True
super ( ) . __init__ ( command_prefix = commands . when_mentioned_or ( " / " ) , intents = intents )
# async def setup_hook(self) -> None:
# self.add_view(OnboardButtons())
#self.add_view(OnboardButtons()) #Add More views with more add_view commands.
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
## Instantiate bot ##
bot = PersistentViewBot ( )
2024-03-26 00:30:05 +00:00
2024-04-10 22:57:07 +00:00
@bot.event
async def on_ready ( ) :
print ( f ' Logged in as { bot . user } ' )
# await welcome_message()
2024-03-26 00:30:05 +00:00
2024-04-10 22:57:07 +00:00
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
c = conn . cursor ( )
print ( " Users in the DB: " )
c . execute ( " SELECT id, username FROM users " )
for row in c . fetchall ( ) :
print ( f ' ID: { row [ 0 ] } User: { row [ 1 ] } ' )
print ( " Servers in the DB: " )
c . execute ( " SELECT id, join_url FROM mc_servers " )
for row in c . fetchall ( ) :
print ( f ' ID: { row [ 0 ] } URL: { row [ 1 ] } ' )
print ( " Permissions in the DB: " )
c . execute ( " SELECT user_id, mc_server_id, permission_level FROM mc_server_users " )
for row in c . fetchall ( ) :
print ( f ' U: { row [ 0 ] } S: { row [ 1 ] } Perm: { row [ 2 ] } ' )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
print ( " Ready. " )
2024-03-26 00:30:05 +00:00
2024-04-10 22:57:07 +00:00
@bot.tree.command ( name = ' addserver ' , description = " Add a Minecraft server to the bot " )
@app_commands.describe (
status_url = " The Minecraft Minder root URL to query/change server status from, like `http://example.com/minecraft.php?password=1234` " ,
join_url = " The Minecraft server hostname and port for people to join, like `minecraft.example.com:25565` "
)
async def addserver ( interaction : discord . Interaction , status_url : str , join_url : str ) :
logging . info ( f ' Addserver: { interaction . user . name } { interaction . user . id } added { status_url } { join_url } ' )
2024-03-26 00:30:05 +00:00
2024-04-10 22:57:07 +00:00
# Connect to the database using a context manager
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
c = conn . cursor ( )
2024-03-26 00:30:05 +00:00
2024-04-10 22:57:07 +00:00
c . execute ( " INSERT OR REPLACE INTO users (id, username) VALUES (?,?) " , ( interaction . user . id , interaction . user . name ) )
c . execute ( " INSERT INTO mc_servers (status_url, join_url) VALUES (?,?) " , ( status_url , join_url ) )
server_id = c . lastrowid
c . execute ( " INSERT OR REPLACE INTO mc_server_users (user_id, mc_server_id, permission_level) VALUES (?,?,?) " ,
( interaction . user . id , server_id , 100 ) )
su_id = c . lastrowid
2024-04-06 03:45:38 +00:00
2024-04-10 22:57:07 +00:00
conn . commit ( )
logging . warning ( f ' Addserver: st: { status_url } jo: { join_url } u: { interaction . user . id } s: { server_id } su: { su_id } ' )
await interaction . response . send_message ( content = " Added! " , ephemeral = True )
2024-04-06 03:45:38 +00:00
2024-04-10 22:57:07 +00:00
@bot.tree.command ( name = ' adduser ' , description = " Give a user privileges to control your Minecraft server " )
@app_commands.describe ( member = " The member you want to add " , server_id = " The internal server ID to add them to " )
async def adduser ( interaction : discord . Interaction , member : discord . Member , server_id : int ) :
logging . info ( f ' Adduser: { member . name } { member . id } { server_id } ' )
2024-03-26 00:30:05 +00:00
2024-04-10 22:57:07 +00:00
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
c = conn . cursor ( )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
c . execute ( " SELECT s.id, su.permission_level FROM mc_servers s JOIN mc_server_users su ON su.user_id = ? AND su.mc_server_id = ? AND su.mc_server_id = s.id " , ( interaction . user . id , server_id ) )
row = c . fetchone ( )
if row :
if row [ 1 ] > = 100 :
c . execute ( " INSERT OR REPLACE INTO users (id, username) VALUES (?,?) " , ( member . id , member . name ) )
c . execute ( " SELECT user_id, mc_server_id FROM mc_server_users WHERE user_id = ? AND mc_server_id = ? " , ( member . id , server_id ) )
if c . fetchone ( ) :
logging . warning ( f ' Adduser: u: { interaction . user . id } m: { member . name } s: { row [ 0 ] } has perm ' )
await interaction . response . send_message ( content = " {} is already added to the server! " . format ( member . mention ) , ephemeral = True )
return
else :
c . execute ( " INSERT OR REPLACE INTO mc_server_users (user_id, mc_server_id, permission_level) VALUES (?,?,?) " ,
( member . id , row [ 0 ] , 1 ) )
conn . commit ( )
logging . warning ( f ' Adduser: u: { interaction . user . id } m: { member . name } s: { row [ 0 ] } ' )
await interaction . response . send_message ( content = " Added {} to the server! " . format ( member . mention ) )
return
else :
logging . error ( f ' Adduser: u: { interaction . user . id } m: { member . name } s: { server_id } privfail ' )
await interaction . response . send_message ( content = " You don ' t have enough privileges to add users to server # {} ! ( {} <100) " . format ( server_id , row [ 1 ] ) , ephemeral = True )
return
else :
logging . error ( f ' Adduser: u: { interaction . user . id } m: { member . name } s: { server_id } permfail ' )
await interaction . response . send_message ( content = " You don ' t have permission to access server # {} ! " . format ( server_id ) , ephemeral = True )
return
@bot.tree.command ( name = ' removeuser ' , description = " Remove a user from the ability to control your Minecraft server " )
@app_commands.describe ( member = " The member you want to remove " , server_id = " The internal server ID to remove them from " )
async def removeuser ( interaction : discord . Interaction , member : discord . Member , server_id : int ) :
logging . info ( f ' Removeuser: { member . name } { member . id } { server_id } ' )
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
2024-04-10 22:57:28 +00:00
c = conn . cursor ( )
2024-04-10 22:57:07 +00:00
c . execute ( " SELECT s.id, su.permission_level FROM mc_servers s JOIN mc_server_users su ON su.user_id = ? AND su.mc_server_id = ? AND su.mc_server_id = s.id " , ( interaction . user . id , server_id ) )
row = c . fetchone ( )
if row :
if row [ 1 ] > = 100 :
c . execute ( " SELECT user_id, mc_server_id FROM mc_server_users WHERE user_id = ? AND mc_server_id = ? " , ( member . id , server_id ) )
if c . fetchone ( ) == None :
logging . warning ( f ' Removeuser: u: { interaction . user . id } m: { member . name } s: { row [ 0 ] } doesn \' t have perm ' )
await interaction . response . send_message ( content = " {} is already removed from the server! " . format ( member . mention ) , ephemeral = True )
return
else :
c . execute ( " DELETE FROM mc_server_users WHERE user_id=? AND mc_server_id=? " , ( member . id , row [ 0 ] ) )
conn . commit ( )
logging . warning ( f ' Removeuser: u: { interaction . user . id } m: { member . name } s: { row [ 0 ] } ' )
await interaction . response . send_message ( content = " Removed {} from the server! " . format ( member . mention ) , ephemeral = True )
return
else :
logging . error ( f ' Removeuser: u: { interaction . user . id } m: { member . name } s: { server_id } privfail ' )
await interaction . response . send_message ( content = " You don ' t have enough privileges to remove users from server # {} ! ( {} <100) " . format ( server_id , row [ 1 ] ) , ephemeral = True )
return
else :
logging . error ( f ' Removeuser: u: { interaction . user . id } m: { member . name } s: { server_id } permfail ' )
await interaction . response . send_message ( content = " You don ' t have permission to access server # {} ! " . format ( server_id ) , ephemeral = True )
return
@bot.tree.command ( name = ' listservers ' , description = " Show the IDs of the MC servers you have access to " )
async def listservers ( interaction : discord . Interaction ) :
logging . info ( f ' Listservers: { interaction . user . name } ' )
# Connect to the database using a context manager
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
c = conn . cursor ( )
c . execute ( " SELECT id, status_url, join_url FROM mc_servers s JOIN mc_server_users su ON su.user_id = ? " , ( interaction . user . id , ) )
o = " "
2024-04-10 22:57:28 +00:00
for row in c . fetchall ( ) :
2024-04-10 22:57:07 +00:00
o + = ' - **ID**: {} \n - **Status URL**: {} \n - **Join URL**: {} \n ' . format ( row [ 0 ] , row [ 1 ] , row [ 2 ] )
if o == " " :
o = " No servers. "
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
await interaction . response . send_message ( content = o , ephemeral = True )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
@bot.tree.command ( name = ' status ' , description = " Minecraft Status " )
async def cmd_status ( interaction : discord . Interaction ) :
logging . warning ( f ' Member { interaction . user } queried Minecraft status. ' )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
c = conn . cursor ( )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
# Todo: select "this guild's/channel's" server not just the first match
c . execute ( " SELECT s.id, su.permission_level, s.status_url, s.join_url FROM mc_servers s JOIN mc_server_users su ON su.user_id = ? AND su.mc_server_id = s.id " , ( interaction . user . id , ) )
row = c . fetchone ( )
if row :
if row [ 1 ] > = 1 :
logging . warning ( f ' Member { interaction . user } started Minecraft. ' )
msg = await interaction . response . defer ( thinking = True )
res = requests . post ( row [ 2 ] , data = { " action " : " status " } )
await interaction . followup . send ( content = f ' { res . text . capitalize ( ) } \n Join at ` { row [ 3 ] } ` ' )
@bot.tree.command ( name = ' start ' , description = " Start Minecraft " )
async def cmd_start ( interaction : discord . Interaction ) :
logging . info ( f ' Start: { interaction . user . name } { interaction . user . id } ' )
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
c = conn . cursor ( )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
# Todo: select "this guild's/channel's" server not just the first match
c . execute ( " SELECT s.id, su.permission_level, s.status_url, s.join_url FROM mc_servers s JOIN mc_server_users su ON su.user_id = ? AND su.mc_server_id = s.id " , ( interaction . user . id , ) )
row = c . fetchone ( )
if row :
if row [ 1 ] > = 1 :
logging . warning ( f ' Member { interaction . user } started Minecraft. ' )
msg = await interaction . response . defer ( thinking = True )
res = requests . post ( row [ 2 ] , data = { " action " : " on " } )
cont = True
count = 0
while cont :
count + = 1
time . sleep ( 5 )
res = requests . post ( row [ 2 ] , data = { " action " : " status " } )
if " running " in res . text :
await interaction . followup . send ( content = f ' Started: { res . text . capitalize ( ) } \n Join at ` { row [ 3 ] } ` ' )
cont = False
elif count > 10 :
await interaction . followup . send ( content = f ' Starting timed out: { res . text . capitalize ( ) } ' )
cont = False
return
else :
logging . error ( f ' Start: u: { interaction . user . id } m: { interaction . user . name } s: { row [ 0 ] } permfail ' )
await interaction . response . send_message ( content = " You don ' t have permission to access server # {} ! " . format ( row [ 0 ] ) , ephemeral = True )
return
else :
logging . error ( f ' Start: u: { interaction . user . id } m: { interaction . user . name } permfail ' )
await interaction . response . send_message ( content = " You don ' t have permission to access any servers! " , ephemeral = True )
return
@bot.command ( name = ' 99 ' )
2024-04-10 22:57:28 +00:00
async def cmd_nine_nine ( ctx ) :
brooklyn_99_quotes = [
' I \' m the human form of the 💯 emoji. ' ,
' Bingpot! ' ,
(
' Cool. Cool cool cool cool cool cool cool, '
' no doubt no doubt no doubt no doubt. '
) ,
]
response = random . choice ( brooklyn_99_quotes )
await ctx . send ( response )
2024-04-10 22:57:07 +00:00
@bot.command ( name = ' shutdown ' )
2024-04-10 22:57:29 +00:00
async def cmd_shutdown ( ctx ) :
""" Shutdown the bot """
await ctx . send ( " Shutting down! " )
sys . exit ( )
2024-04-10 22:57:07 +00:00
@bot.command ( name = ' sync ' )
async def cmd_sync ( ctx ) :
synced = await bot . tree . sync ( )
o = f ' { len ( synced ) } / { len ( bot . tree . get_commands ( ) ) } Commands Synced: ( { " , " . join ( [ str ( o . name ) for o in synced ] ) } ) '
await ctx . send ( o , ephemeral = True )
logging . warning ( o )
2024-04-10 22:57:28 +00:00
2024-04-10 22:57:07 +00:00
@bot.event
2024-04-02 22:45:34 +00:00
async def on_member_remove ( member ) :
# Log member leave
2024-04-10 22:57:28 +00:00
logging . warning ( f ' Member { member . name } left the server. ' )
2024-04-02 22:45:34 +00:00
2024-04-10 22:57:07 +00:00
## Runtime ##
# Connect to SQLite database
with sqlite3 . connect ( ' bot_data.db ' ) as conn :
c = conn . cursor ( )
# Create table with fields if it doesn't exist already
c . execute ( ''' CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY ,
username TEXT
) ''' )
c . execute ( ''' CREATE TABLE IF NOT EXISTS mc_servers (
id INTEGER PRIMARY KEY AUTOINCREMENT ,
status_url TEXT ,
join_url TEXT
) ''' )
c . execute ( ''' CREATE TABLE IF NOT EXISTS mc_server_users (
user_id TEXT ,
mc_server_id TEXT ,
permission_level INTEGER ,
FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
FOREIGN KEY ( mc_server_id ) REFERENCES mc_servers ( id ) ,
PRIMARY KEY ( user_id , mc_server_id )
) ''' )
conn . commit ( )
2024-03-26 00:30:05 +00:00
2024-04-02 22:45:34 +00:00
# Start bot
2024-04-10 22:57:07 +00:00
bot . run ( TOKEN )