Bunch of cleanup
This commit is contained in:
parent
dee736bd54
commit
65a504c882
4 changed files with 282 additions and 246 deletions
|
|
@ -11,4 +11,4 @@ RUN apt update && apt install sqlite3
|
||||||
RUN pip install --upgrade pip && pip install -r requirements.txt
|
RUN pip install --upgrade pip && pip install -r requirements.txt
|
||||||
|
|
||||||
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
||||||
CMD [ "python3", "mstbot.py" ]
|
CMD [ "python3", "-u", "mstbot.py" ]
|
||||||
|
|
|
||||||
24
README.md
24
README.md
|
|
@ -1,2 +1,24 @@
|
||||||
|

|
||||||
# MinecraftStatus
|
# MinecraftStatus
|
||||||
Minecraft Status discord bot
|
Minecraft Status discord bot that adds status banners to your guild's sidebar. See below for example.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Commands
|
||||||
|
```
|
||||||
|
Admin:
|
||||||
|
announce Turns on player join announcements
|
||||||
|
cleanup Removes querier and status channels
|
||||||
|
noannounce Turns off player join announcements
|
||||||
|
nohours Turns off total player hour display
|
||||||
|
setup Sets up new Minecraft server querier
|
||||||
|
showhours Turns on total player hour display
|
||||||
|
Other:
|
||||||
|
help Shows this message
|
||||||
|
lastquery Shows last query time
|
||||||
|
players Lists online players
|
||||||
|
status Shows server status
|
||||||
|
|
||||||
|
Type $help command for more info on a command.
|
||||||
|
You can also type $help category for more info on a category.
|
||||||
|
```
|
||||||
|
|
|
||||||
497
mstbot.py
497
mstbot.py
|
|
@ -1,9 +1,10 @@
|
||||||
# TODO: Change hours column to boolean, drop times table, make it a column in servers
|
# TODO: in readme, add badge for guild count, total player count
|
||||||
# TODO: prevent sql injection
|
# TODO: make dev bot for testing
|
||||||
|
# TODO: handle wait=301 rate limits more cleanly
|
||||||
# TODO: annotate return types everywhere
|
# TODO: annotate return types everywhere
|
||||||
# TODO: split cogs into separate files, add utils file, remove globals
|
# TODO: split cogs into separate files, add utils file, remove globals
|
||||||
|
# TODO: wrap all SQL queries with a func to determine if server has been set up or not. If fetchone returns None, log helpful message to run $setup.
|
||||||
# TODO: save invite link to README and show query port enable instructions
|
# TODO: save invite link to README and show query port enable instructions
|
||||||
# TODO: https://discord.com/oauth2/authorize?client_id=911009295947165747&permissions=3088&response_type=code&redirect_uri=https%3A%2F%2Fwww.benrmorgan.com%2Fportfolio%2Fminecraft-server-tools-bot&integration_type=0&scope=messages.read+bot
|
|
||||||
# TODO: Feature: allow discord users to associate their mc usernames, change server nickname?
|
# TODO: Feature: allow discord users to associate their mc usernames, change server nickname?
|
||||||
# TODO: Feature: allow other users to query playtime by username, default to their own. This and above require new columns in names table.
|
# TODO: Feature: allow other users to query playtime by username, default to their own. This and above require new columns in names table.
|
||||||
|
|
||||||
|
|
@ -13,6 +14,7 @@ import re
|
||||||
import socket
|
import socket
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Union, List
|
from typing import Union, List
|
||||||
|
|
||||||
|
|
@ -31,10 +33,11 @@ servers = []
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
print(f'Logged in as {bot.user}')
|
print(f'Logged in as {bot.user}')
|
||||||
mydb, cursor = connect()
|
|
||||||
|
|
||||||
cursor.execute("SELECT id, ip, announce_joins, announce_joins_id FROM servers")
|
with sqlite_conn() as conn:
|
||||||
rows = cursor.fetchall()
|
rows = conn.execute(
|
||||||
|
"SELECT id, ip, announce_joins, announce_joins_id FROM servers"
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if get_server_by_id(row[0]) is None:
|
if get_server_by_id(row[0]) is None:
|
||||||
|
|
@ -51,14 +54,13 @@ async def on_ready():
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_command_error(ctx: commands.Context, error: commands.CommandError):
|
async def on_command_error(ctx: commands.Context, error: commands.CommandError):
|
||||||
command = ctx.message.content.split()[0][1:] # Extract command name
|
command = ctx.message.content.split()[0][1:] # Extract command name
|
||||||
prefix = ctx.prefix
|
|
||||||
|
|
||||||
if isinstance(error, commands.CommandNotFound):
|
if isinstance(error, commands.CommandNotFound):
|
||||||
await safe_send(ctx, f'Command "{command}" does not exist. Use `{ctx.prefix}help` for available commands.')
|
await safe_send(f'Command "{command}" does not exist. Use `{ctx.prefix}help` for available commands.', ctx)
|
||||||
elif isinstance(error, commands.MissingRequiredArgument):
|
elif isinstance(error, commands.MissingRequiredArgument):
|
||||||
await log(ctx, f'Command "{command}" is missing required arguments. Use `{ctx.prefix}help {command}` for details.')
|
await log(ctx, f'Command "{command}" is missing required arguments. Use `{ctx.prefix}help {command}` for details.')
|
||||||
elif isinstance(error, commands.MissingPermissions):
|
elif isinstance(error, commands.MissingPermissions):
|
||||||
await safe_send(ctx, f'You lack permissions: {", ".join(error.missing_perms)}.')
|
await safe_send(f'You lack permissions: {", ".join(error.missing_perms)}.', ctx)
|
||||||
elif isinstance(error, commands.CommandInvokeError) and isinstance(error.original, socket.gaierror):
|
elif isinstance(error, commands.CommandInvokeError) and isinstance(error.original, socket.gaierror):
|
||||||
await log(ctx, "Could not connect, is the server down?")
|
await log(ctx, "Could not connect, is the server down?")
|
||||||
else:
|
else:
|
||||||
|
|
@ -87,16 +89,16 @@ class Admin(commands.Cog):
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
await log(ctx, "Setup query failed, server down?")
|
await log(ctx, "Setup query failed, server down?")
|
||||||
else:
|
else:
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
|
conn.execute("""
|
||||||
|
INSERT INTO servers (id, ip, port)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
ip = excluded.ip,
|
||||||
|
port = excluded.port
|
||||||
|
""", (ctx.guild.id, val_ip, val_port))
|
||||||
|
|
||||||
cursor.execute(f"INSERT INTO servers (id, ip, port) VALUES({str(ctx.guild.id)}, \"{val_ip}\", {str(port)}) ON CONFLICT(id) DO UPDATE SET ip=\"{val_ip}\", port={str(port)}")
|
setMCNames(ctx.guild.id, conn, names)
|
||||||
|
|
||||||
setMCNames(ctx.guild.id, cursor, names)
|
|
||||||
|
|
||||||
cursor.execute(f"INSERT INTO times (id) VALUES({ctx.guild.id}) ON CONFLICT(id) DO UPDATE SET id=id;")
|
|
||||||
|
|
||||||
mydb.commit()
|
|
||||||
mydb.close()
|
|
||||||
|
|
||||||
if annChanID is None:
|
if annChanID is None:
|
||||||
await setAnn(ctx, False)
|
await setAnn(ctx, False)
|
||||||
|
|
@ -129,34 +131,12 @@ class Admin(commands.Cog):
|
||||||
@commands.command(brief="Turns on total player hour display", description="Turns on total player hour display.")
|
@commands.command(brief="Turns on total player hour display", description="Turns on total player hour display.")
|
||||||
@commands.has_permissions(administrator=True)
|
@commands.has_permissions(administrator=True)
|
||||||
async def showhours(self, ctx: discord.ext.commands.context.Context):
|
async def showhours(self, ctx: discord.ext.commands.context.Context):
|
||||||
await setHours(ctx, True)
|
await setShowHours(ctx, True)
|
||||||
|
|
||||||
@commands.command(brief="Turns off total player hour display", description="Turns off total player hour display.")
|
@commands.command(brief="Turns off total player hour display", description="Turns off total player hour display.")
|
||||||
@commands.has_permissions(administrator=True)
|
@commands.has_permissions(administrator=True)
|
||||||
async def nohours(self, ctx: discord.ext.commands.context.Context):
|
async def nohours(self, ctx: discord.ext.commands.context.Context):
|
||||||
await setHours(ctx, False)
|
await setShowHours(ctx, False)
|
||||||
|
|
||||||
# @commands.command()
|
|
||||||
# async def notif(ctx: discord.ext.commands.context.Context, kind: Union[str, None] = None):
|
|
||||||
# if kind is None:
|
|
||||||
# await safe_send(ctx, "The notification options are: \n1. joins")
|
|
||||||
# else:
|
|
||||||
# name = ""
|
|
||||||
# if kind.lower() == "joins":
|
|
||||||
# name = "Status Joins"
|
|
||||||
#
|
|
||||||
# if name != "":
|
|
||||||
# await addrole(ctx, name)
|
|
||||||
# await log(ctx, "Gave", name, "role to", ctx.message.author.name)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# async def addrole(ctx: discord.ext.commands.context.Context, name: str):
|
|
||||||
# member = ctx.message.author
|
|
||||||
# role = get(member.guild.roles, name=name)
|
|
||||||
# if role is None:
|
|
||||||
# role = await member.guild.create_role(name=name)
|
|
||||||
#
|
|
||||||
# await member.add_roles(role)
|
|
||||||
|
|
||||||
|
|
||||||
class Other(commands.Cog):
|
class Other(commands.Cog):
|
||||||
|
|
@ -167,33 +147,29 @@ class Other(commands.Cog):
|
||||||
|
|
||||||
@commands.command(brief="Shows server status", description="Shows server status.")
|
@commands.command(brief="Shows server status", description="Shows server status.")
|
||||||
async def status(self, ctx: discord.ext.commands.context.Context):
|
async def status(self, ctx: discord.ext.commands.context.Context):
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
ip, port = getMCIP(ctx.guild.id, cursor)
|
ip, port = getMCIP(ctx.guild.id, conn)
|
||||||
mc = JavaServer(ip, port)
|
mc = JavaServer(ip, port)
|
||||||
|
|
||||||
|
|
||||||
if mc is None:
|
|
||||||
await safe_send("There is no server query set up. Run the `setup` command to get started.", ctx=ctx)
|
|
||||||
else:
|
|
||||||
players, max, _, motd = await getStatus(mc)
|
|
||||||
|
|
||||||
seconds = getPersonSeconds(ctx.guild.id, cursor)
|
|
||||||
seconds = seconds % (24 * 3600)
|
|
||||||
hour = seconds // 3600
|
|
||||||
seconds %= 3600
|
|
||||||
minutes = seconds // 60
|
|
||||||
seconds %= 60
|
|
||||||
|
|
||||||
await safe_send("Status:\n " + ip + "\n " + motd.to_plain() + "\n\n Players: " + str(players) + "/" + str(max) + "\n Total Player Time: " + "%d:%02d:%02d" % (hour, minutes, seconds), ctx=ctx, format="```")
|
|
||||||
|
|
||||||
mydb.close()
|
if mc is None:
|
||||||
|
await safe_send("There is no server query set up. Run the `setup` command to get started.", ctx=ctx)
|
||||||
|
else:
|
||||||
|
players, max, _, motd = await getStatus(mc)
|
||||||
|
|
||||||
|
seconds = getPersonSeconds(ctx.guild.id, conn)
|
||||||
|
seconds = seconds % (24 * 3600)
|
||||||
|
hour = seconds // 3600
|
||||||
|
seconds %= 3600
|
||||||
|
minutes = seconds // 60
|
||||||
|
seconds %= 60
|
||||||
|
|
||||||
|
await safe_send("Status:\n " + ip + "\n " + motd.to_plain() + "\n\n Players: " + str(players) + "/" + str(max) + "\n Total Player Time: " + "%d:%02d:%02d" % (hour, minutes, seconds), ctx=ctx, format="```")
|
||||||
|
|
||||||
@commands.command(brief="Lists online players", description="Lists online players.")
|
@commands.command(brief="Lists online players", description="Lists online players.")
|
||||||
async def players(self, ctx: discord.ext.commands.context.Context):
|
async def players(self, ctx: discord.ext.commands.context.Context):
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
ip, port = getMCIP(ctx.guild.id, cursor)
|
ip, port = getMCIP(ctx.guild.id, conn)
|
||||||
mc = JavaServer(ip, port)
|
mc = JavaServer(ip, port)
|
||||||
mydb.close()
|
|
||||||
|
|
||||||
if mc is None:
|
if mc is None:
|
||||||
await safe_send("There is no server query set up. Run the `setup` command to get started.", ctx=ctx)
|
await safe_send("There is no server query set up. Run the `setup` command to get started.", ctx=ctx)
|
||||||
|
|
@ -209,11 +185,10 @@ class Other(commands.Cog):
|
||||||
|
|
||||||
@commands.command(brief="Shows last query time", description="Shows last query time. Useful for debugging server connection issues.")
|
@commands.command(brief="Shows last query time", description="Shows last query time. Useful for debugging server connection issues.")
|
||||||
async def lastquery(self, ctx: discord.ext.commands.context.Context):
|
async def lastquery(self, ctx: discord.ext.commands.context.Context):
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
ip, port = getMCIP(ctx.guild.id, cursor)
|
ip, port = getMCIP(ctx.guild.id, conn)
|
||||||
mc = JavaServer(ip, port)
|
mc = JavaServer(ip, port)
|
||||||
last = getMCQueryTime(ctx.guild.id, cursor)
|
last = getMCQueryTime(ctx.guild.id, conn)
|
||||||
mydb.close()
|
|
||||||
|
|
||||||
if mc is None:
|
if mc is None:
|
||||||
await safe_send("There is no server query set up. Run the `setup` command to get started.", ctx=ctx)
|
await safe_send("There is no server query set up. Run the `setup` command to get started.", ctx=ctx)
|
||||||
|
|
@ -236,15 +211,20 @@ async def setAnn(ctx: discord.ext.commands.context.Context, ann: bool, cid: Unio
|
||||||
await log(ctx, "Channel", str(cid), "does not exist.")
|
await log(ctx, "Channel", str(cid), "does not exist.")
|
||||||
return
|
return
|
||||||
|
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
ip, _ = getMCIP(ctx.guild.id, cursor)
|
ip, _ = getMCIP(ctx.guild.id, conn)
|
||||||
|
|
||||||
cursor.execute(
|
conn.execute("""
|
||||||
"UPDATE servers SET announce_joins=" + str((0, 1)[ann]) + ", announce_joins_id=" + ("NULL", str(cid))[
|
UPDATE servers
|
||||||
ann] + " WHERE id=" + str(ctx.guild.id))
|
SET
|
||||||
|
announce_joins = ?,
|
||||||
mydb.commit()
|
announce_joins_id = ?
|
||||||
mydb.close()
|
WHERE id = ?
|
||||||
|
""", (
|
||||||
|
ann,
|
||||||
|
cid if ann else None,
|
||||||
|
ctx.guild.id
|
||||||
|
))
|
||||||
|
|
||||||
await log(ctx, (
|
await log(ctx, (
|
||||||
"Not announcing when a player joins " + ip + ".",
|
"Not announcing when a player joins " + ip + ".",
|
||||||
|
|
@ -252,20 +232,23 @@ async def setAnn(ctx: discord.ext.commands.context.Context, ann: bool, cid: Unio
|
||||||
ann])
|
ann])
|
||||||
|
|
||||||
|
|
||||||
async def setHours(ctx: discord.ext.commands.context.Context, hours: bool):
|
async def setShowHours(ctx: discord.ext.commands.context.Context, show_hours: bool):
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
ip, _ = getMCIP(ctx.guild.id, cursor)
|
ip, _ = getMCIP(ctx.guild.id, conn)
|
||||||
|
|
||||||
cursor.execute(
|
conn.execute("""
|
||||||
f"UPDATE servers SET hours={str((0, 1)[hours])} WHERE id={str(ctx.guild.id)}")
|
UPDATE servers
|
||||||
|
SET show_hours = ?
|
||||||
mydb.commit()
|
WHERE id = ?
|
||||||
mydb.close()
|
""", (
|
||||||
|
show_hours,
|
||||||
|
ctx.guild.id
|
||||||
|
))
|
||||||
|
|
||||||
await log(ctx, (
|
await log(ctx, (
|
||||||
"Not displaying total player hours for " + ip + ".",
|
"Not displaying total player hours for " + ip + ".",
|
||||||
"Displaying total player hours for " + ip + ".")[
|
"Displaying total player hours for " + ip + ".")[
|
||||||
hours] + " Please wait a moment for channel updates to take effect.")
|
show_hours] + " Please wait a moment for channel updates to take effect.")
|
||||||
|
|
||||||
|
|
||||||
async def log(ctx: commands.Context, *msg: str):
|
async def log(ctx: commands.Context, *msg: str):
|
||||||
|
|
@ -349,77 +332,110 @@ async def getStatus(serv: JavaServer):
|
||||||
return p, m, n, d
|
return p, m, n, d
|
||||||
|
|
||||||
|
|
||||||
def connect():
|
@contextmanager
|
||||||
mydb = sqlite3.connect('/data/mstbot.db')
|
def sqlite_conn():
|
||||||
|
conn = sqlite3.connect('/data/mstbot.db')
|
||||||
cursor = mydb.cursor()
|
try:
|
||||||
return mydb, cursor
|
# per-connection PRAGMAs
|
||||||
|
conn.execute("PRAGMA foreign_keys = ON")
|
||||||
|
conn.execute("PRAGMA busy_timeout = 5000")
|
||||||
|
yield conn
|
||||||
|
conn.commit()
|
||||||
|
except Exception:
|
||||||
|
conn.rollback()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
# run on close
|
||||||
|
conn.execute("PRAGMA optimize")
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def getMCIP(sid: int, cursor: sqlite3.Cursor):
|
def getMCIP(sid: int, conn: sqlite3.Connection):
|
||||||
cursor.execute("SELECT ip, port FROM servers WHERE id=" + str(sid))
|
row = conn.execute(
|
||||||
row = cursor.fetchone()
|
"SELECT ip, port FROM servers WHERE id = ?",
|
||||||
|
(sid,)
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
ip = row[0]
|
ip = row[0]
|
||||||
port = row[1]
|
port = row[1]
|
||||||
|
|
||||||
return ip, port
|
return ip, port
|
||||||
|
|
||||||
|
|
||||||
def getMCNames(sid: int, cursor: sqlite3.Cursor):
|
def getMCNames(sid: int, conn: sqlite3.Connection):
|
||||||
cursor.execute("SELECT name FROM names WHERE id=" + str(sid))
|
rows = conn.execute(
|
||||||
rows = cursor.fetchall()
|
"SELECT name FROM names WHERE id = ?",
|
||||||
|
(sid,)
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
return [row[0] for row in rows]
|
return [row[0] for row in rows]
|
||||||
|
|
||||||
|
|
||||||
def getMCQueryTime(sid: int, cursor: sqlite3.Cursor):
|
def getMCQueryTime(sid: int, conn: sqlite3.Connection):
|
||||||
cursor.execute("SELECT last_query FROM servers WHERE id=" + str(sid))
|
tstr = conn.execute(
|
||||||
tstr = cursor.fetchone()[0]
|
"SELECT last_query FROM servers WHERE id = ?",
|
||||||
|
(sid,)
|
||||||
|
).fetchone()[0]
|
||||||
|
|
||||||
if tstr is None:
|
if tstr is None:
|
||||||
return tstr
|
return tstr
|
||||||
|
|
||||||
return datetime.strptime(tstr, '%Y-%m-%d %H:%M:%S')
|
return datetime.strptime(tstr, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
def getPersonSeconds(sid: int, cursor: sqlite3.Cursor):
|
def getPersonSeconds(sid: int, conn: sqlite3.Connection):
|
||||||
cursor.execute("SELECT time FROM times WHERE id=" + str(sid))
|
cols = conn.execute(
|
||||||
tstr = cursor.fetchone()[0]
|
f"SELECT SUM(seconds) FROM times WHERE id = ? GROUP BY id",
|
||||||
|
(sid,)
|
||||||
|
).fetchone()
|
||||||
|
if cols is not None:
|
||||||
|
return cols[0]
|
||||||
|
|
||||||
return int(tstr)
|
return 0
|
||||||
|
|
||||||
def getShowHours(sid: int, cursor: sqlite3.Cursor):
|
def getShowHours(sid: int, conn: sqlite3.Connection):
|
||||||
cursor.execute("SELECT hours FROM servers WHERE id=" + str(sid))
|
show_hours = conn.execute(
|
||||||
tstr = cursor.fetchone()[0]
|
"SELECT show_hours FROM servers WHERE id = ?",
|
||||||
|
(sid,)
|
||||||
|
).fetchone()[0]
|
||||||
|
|
||||||
return bool(tstr)
|
return show_hours
|
||||||
|
|
||||||
def getMCJoinAnnounce(sid: int, cursor: sqlite3.Cursor):
|
def getMCJoinAnnounce(sid: int, conn: sqlite3.Connection):
|
||||||
cursor.execute("SELECT announce_joins, announce_joins_id FROM servers WHERE id=" + str(sid))
|
row = conn.execute(
|
||||||
row = cursor.fetchone()
|
"SELECT announce_joins, announce_joins_id FROM servers WHERE id = ?",
|
||||||
|
(sid,)
|
||||||
|
).fetchone()
|
||||||
ann = row[0]
|
ann = row[0]
|
||||||
cid = row[1]
|
cid = row[1]
|
||||||
|
|
||||||
return ann, cid
|
return ann, cid
|
||||||
|
|
||||||
|
|
||||||
def setMCNames(sid: int, cursor: sqlite3.Cursor, names: List[str]):
|
def setMCNames(sid: int, conn: sqlite3.Connection, names: List[str]):
|
||||||
cursor.execute("DELETE FROM names WHERE id=" + str(sid))
|
conn.execute("DELETE FROM names WHERE id = ?", (sid,))
|
||||||
|
|
||||||
if len(names) > 0:
|
if names:
|
||||||
qStr = ""
|
rows = [(sid, name) for name in names]
|
||||||
for name in names:
|
conn.executemany("INSERT INTO names (id, name) VALUES (?, ?)", rows)
|
||||||
qStr += "(" + str(sid) + ",\"" + name + "\"),"
|
|
||||||
|
|
||||||
qStr = qStr[:-1] + ";"
|
def setMCQueryTime(sid: int, conn: sqlite3.Connection, dt: datetime):
|
||||||
|
conn.execute("""
|
||||||
cursor.execute("INSERT INTO names (id, name) VALUES " + qStr)
|
UPDATE servers
|
||||||
|
SET last_query = ?
|
||||||
|
WHERE id = ?
|
||||||
|
""", (
|
||||||
|
dt.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
sid
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
def setMCQueryTime(sid: int, cursor: sqlite3.Cursor, dt: datetime):
|
def incrementPersonSeconds(sid: int, user: str, conn: sqlite3.Connection, seconds: float):
|
||||||
cursor.execute("UPDATE servers SET last_query=\"" + dt.strftime('%Y-%m-%d %H:%M:%S') + "\" WHERE id=" + str(sid))
|
conn.execute("""
|
||||||
|
INSERT INTO times (id, name, seconds)
|
||||||
def incrementPersonSeconds(sid: int, cursor: sqlite3.Cursor, seconds: int):
|
VALUES (?, ?, ?)
|
||||||
cursor.execute(f"UPDATE times SET time=time+{seconds} WHERE id={str(sid)};")
|
ON CONFLICT(id, name) DO UPDATE SET
|
||||||
|
seconds = times.seconds + excluded.seconds
|
||||||
|
""", (sid, user, seconds))
|
||||||
|
|
||||||
async def do_bot_cleanup(sid: int, ctx: Union[discord.ext.commands.context.Context, None] = None):
|
async def do_bot_cleanup(sid: int, ctx: Union[discord.ext.commands.context.Context, None] = None):
|
||||||
if sid in servers:
|
if sid in servers:
|
||||||
|
|
@ -440,16 +456,13 @@ async def do_bot_cleanup(sid: int, ctx: Union[discord.ext.commands.context.Conte
|
||||||
except discord.errors.Forbidden:
|
except discord.errors.Forbidden:
|
||||||
print(currServ.name, "Error: I don't have permission to delete channels.")
|
print(currServ.name, "Error: I don't have permission to delete channels.")
|
||||||
|
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
ip, port = getMCIP(sid, cursor)
|
ip, port = getMCIP(sid, conn)
|
||||||
mc = JavaServer(ip, port)
|
mc = JavaServer(ip, port)
|
||||||
|
|
||||||
cursor.execute("DELETE FROM servers WHERE id=" + str(sid))
|
conn.execute("DELETE FROM servers WHERE id = ?", (sid,))
|
||||||
cursor.execute("DELETE FROM names WHERE id=" + str(sid))
|
conn.execute("DELETE FROM names WHERE id = ?", (sid,))
|
||||||
cursor.execute("DELETE FROM times WHERE id=" + str(sid))
|
conn.execute("DELETE FROM times WHERE id = ?", (sid,))
|
||||||
|
|
||||||
mydb.commit()
|
|
||||||
mydb.close()
|
|
||||||
|
|
||||||
if ctx is None:
|
if ctx is None:
|
||||||
print("Cleaned up", sid, ".")
|
print("Cleaned up", sid, ".")
|
||||||
|
|
@ -496,120 +509,82 @@ async def status_task(sid: int):
|
||||||
print(get_server_by_id(sid).name, "is not querying. Ending task.")
|
print(get_server_by_id(sid).name, "is not querying. Ending task.")
|
||||||
return
|
return
|
||||||
|
|
||||||
mydb, cursor = connect()
|
with sqlite_conn() as conn:
|
||||||
ip, port = getMCIP(sid, cursor)
|
ip, port = getMCIP(sid, conn)
|
||||||
mc = JavaServer(ip, port)
|
mc = JavaServer(ip, port)
|
||||||
currServ = get_server_by_id(sid)
|
currServ = get_server_by_id(sid)
|
||||||
wait = 10
|
wait = 10
|
||||||
|
|
||||||
if not ip == "" and currServ is not None:
|
if not ip == "" and currServ is not None:
|
||||||
try:
|
try:
|
||||||
oldNames = getMCNames(sid, cursor)
|
oldNames = getMCNames(sid, conn)
|
||||||
players, max, names, _ = await getStatus(mc)
|
players, max, names, _ = await getStatus(mc)
|
||||||
lastTime = getMCQueryTime(sid, cursor)
|
lastTime = getMCQueryTime(sid, conn)
|
||||||
lastSeconds = getPersonSeconds(sid, cursor)
|
lastSeconds = getPersonSeconds(sid, conn)
|
||||||
|
|
||||||
currTime = datetime.now(timezone.utc)
|
currTime = datetime.now(timezone.utc)
|
||||||
if lastTime is None:
|
if lastTime is None:
|
||||||
lastTime = currTime
|
lastTime = currTime
|
||||||
else:
|
else:
|
||||||
lastTime = lastTime.replace(tzinfo=timezone.utc)
|
lastTime = lastTime.replace(tzinfo=timezone.utc)
|
||||||
|
|
||||||
print(currServ.name, "Query:", ip, str(players) + "/" + str(max), datetime.now(timezone.utc).strftime("%H:%M:%S"),
|
|
||||||
str(currTime - lastTime))
|
|
||||||
|
|
||||||
setMCNames(sid, cursor, names)
|
deltaSeconds = (currTime - lastTime).total_seconds()
|
||||||
setMCQueryTime(sid, cursor, currTime)
|
|
||||||
if not first_iter:
|
print(currServ.name, "Query:", ip, str(players) + "/" + str(max), datetime.now(timezone.utc).strftime("%H:%M:%S"),
|
||||||
incrementPersonSeconds(sid, cursor, len(names) * (currTime - lastTime).total_seconds())
|
str(deltaSeconds))
|
||||||
|
|
||||||
announceJoin, annChanId = getMCJoinAnnounce(sid, cursor)
|
setMCNames(sid, conn, names)
|
||||||
annRole = None
|
setMCQueryTime(sid, conn, currTime)
|
||||||
|
if not first_iter:
|
||||||
|
for name in names:
|
||||||
|
incrementPersonSeconds(sid, name, conn, deltaSeconds)
|
||||||
|
|
||||||
if announceJoin and annChanId is not None:
|
announceJoin, annChanId = getMCJoinAnnounce(sid, conn)
|
||||||
jNames = list(set(names) - set(oldNames))
|
annRole = None
|
||||||
for name in jNames:
|
|
||||||
aChannels = find_channels(serv=currServ, chanid=int(annChanId))
|
|
||||||
|
|
||||||
if len(aChannels) > 0:
|
if announceJoin and annChanId is not None:
|
||||||
if annRole is None:
|
jNames = list(set(names) - set(oldNames))
|
||||||
await safe_send(name + " joined the game!", chan=aChannels[0])
|
for name in jNames:
|
||||||
else:
|
aChannels = find_channels(serv=currServ, chanid=int(annChanId))
|
||||||
await safe_send("<@&" + str(annRole.id) + "> " + name + " joined the game!",
|
|
||||||
chan=aChannels[0])
|
|
||||||
|
|
||||||
print(currServ.name, "Announced player(s) join.")
|
if len(aChannels) > 0:
|
||||||
|
if annRole is None:
|
||||||
|
await safe_send(name + " joined the game!", chan=aChannels[0])
|
||||||
|
else:
|
||||||
|
await safe_send("<@&" + str(annRole.id) + "> " + name + " joined the game!",
|
||||||
|
chan=aChannels[0])
|
||||||
|
|
||||||
except asyncio.exceptions.TimeoutError:
|
print(currServ.name, "Announced player(s) join.")
|
||||||
print(currServ.name, "Timeout, server lagging?")
|
|
||||||
players = -1
|
|
||||||
lastSeconds = -1
|
|
||||||
|
|
||||||
except (ConnectionRefusedError, socket.gaierror):
|
except asyncio.exceptions.TimeoutError:
|
||||||
print(currServ.name, "Cannot connect to server, down?")
|
print(currServ.name, "Timeout, server lagging?")
|
||||||
players = -1
|
players = -1
|
||||||
lastSeconds = -1
|
lastSeconds = -1
|
||||||
|
|
||||||
iChannels = find_channels(serv=currServ, channame="IP: ", channamesearch="in", chantype=discord.ChannelType.voice)
|
except (ConnectionRefusedError, socket.gaierror):
|
||||||
pChannels = find_channels(serv=currServ, channame="Players: ", channamesearch="in",
|
print(currServ.name, "Cannot connect to server, down?")
|
||||||
chantype=discord.ChannelType.voice)
|
players = -1
|
||||||
tChannels = find_channels(serv=currServ, channame="Player Hrs: ", channamesearch="in",
|
lastSeconds = -1
|
||||||
chantype=discord.ChannelType.voice)
|
|
||||||
|
|
||||||
overwrites = {
|
iChannels = find_channels(serv=currServ, channame="IP: ", channamesearch="in", chantype=discord.ChannelType.voice)
|
||||||
currServ.default_role: discord.PermissionOverwrite(connect=False, view_channel=True),
|
pChannels = find_channels(serv=currServ, channame="Players: ", channamesearch="in",
|
||||||
currServ.me: discord.PermissionOverwrite(connect=True, view_channel=True, manage_channels=True)
|
chantype=discord.ChannelType.voice)
|
||||||
}
|
tChannels = find_channels(serv=currServ, channame="Player Hrs: ", channamesearch="in",
|
||||||
|
chantype=discord.ChannelType.voice)
|
||||||
|
|
||||||
if len(iChannels) > 0:
|
overwrites = {
|
||||||
lastIPName = iChannels[0].name
|
currServ.default_role: discord.PermissionOverwrite(connect=False, view_channel=True),
|
||||||
|
currServ.me: discord.PermissionOverwrite(connect=True, view_channel=True, manage_channels=True)
|
||||||
|
}
|
||||||
|
|
||||||
ipStr = "IP: " + ip
|
if len(iChannels) > 0:
|
||||||
if lastIPName != ipStr:
|
lastIPName = iChannels[0].name
|
||||||
try:
|
|
||||||
print(currServ.name, "Update: Ip changed!")
|
|
||||||
await iChannels[0].edit(name=ipStr)
|
|
||||||
wait = 301
|
|
||||||
except discord.errors.Forbidden:
|
|
||||||
print(currServ.name,
|
|
||||||
"Error: I don't have permission to edit channels. Try deleting the channels I create. Then, run the `setup` command again.")
|
|
||||||
await do_bot_cleanup(sid)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
await currServ.create_voice_channel("IP: " + ip, overwrites=overwrites)
|
|
||||||
|
|
||||||
if len(pChannels) > 0:
|
ipStr = "IP: " + ip
|
||||||
lastPName = pChannels[0].name
|
if lastIPName != ipStr:
|
||||||
|
|
||||||
if players == -1:
|
|
||||||
pStr = lastPName
|
|
||||||
else:
|
|
||||||
pStr = "Players: " + str(players) + "/" + str(max)
|
|
||||||
|
|
||||||
if lastPName != pStr:
|
|
||||||
try:
|
|
||||||
print(currServ.name, "Update: Players changed!")
|
|
||||||
await pChannels[0].edit(name=pStr)
|
|
||||||
wait = 301
|
|
||||||
except discord.errors.Forbidden:
|
|
||||||
print(currServ.name,
|
|
||||||
"Error: I don't have permission to edit channels. Try deleting the channels I create. Then, run the `setup` command again.")
|
|
||||||
await do_bot_cleanup(sid)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
await currServ.create_voice_channel(f"Players: {str(players)}/{str(max)}",
|
|
||||||
overwrites=overwrites)
|
|
||||||
|
|
||||||
do_show_hours = getShowHours(sid, cursor)
|
|
||||||
if do_show_hours and not first_iter and lastSeconds != -1:
|
|
||||||
tStr = "Player Hrs: " + str(round((lastSeconds + len(names) * (currTime - lastTime).total_seconds())/3600))
|
|
||||||
if len(tChannels) > 0:
|
|
||||||
lastTName = tChannels[0].name
|
|
||||||
|
|
||||||
if lastTName != tStr:
|
|
||||||
try:
|
try:
|
||||||
print(currServ.name, "Update: Time changed!")
|
print(currServ.name, "Update: Ip changed!")
|
||||||
await tChannels[0].edit(name=tStr)
|
await iChannels[0].edit(name=ipStr)
|
||||||
wait = 301
|
wait = 301
|
||||||
except discord.errors.Forbidden:
|
except discord.errors.Forbidden:
|
||||||
print(currServ.name,
|
print(currServ.name,
|
||||||
|
|
@ -617,15 +592,53 @@ async def status_task(sid: int):
|
||||||
await do_bot_cleanup(sid)
|
await do_bot_cleanup(sid)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
await currServ.create_voice_channel(tStr,
|
await currServ.create_voice_channel("IP: " + ip, overwrites=overwrites)
|
||||||
overwrites=overwrites)
|
|
||||||
elif not do_show_hours:
|
|
||||||
if len(tChannels) > 0:
|
|
||||||
for channel in tChannels:
|
|
||||||
await channel.delete()
|
|
||||||
|
|
||||||
mydb.commit()
|
if len(pChannels) > 0:
|
||||||
mydb.close()
|
lastPName = pChannels[0].name
|
||||||
|
|
||||||
|
if players == -1:
|
||||||
|
pStr = lastPName
|
||||||
|
else:
|
||||||
|
pStr = "Players: " + str(players) + "/" + str(max)
|
||||||
|
|
||||||
|
if lastPName != pStr:
|
||||||
|
try:
|
||||||
|
print(currServ.name, "Update: Players changed!")
|
||||||
|
await pChannels[0].edit(name=pStr)
|
||||||
|
wait = 301
|
||||||
|
except discord.errors.Forbidden:
|
||||||
|
print(currServ.name,
|
||||||
|
"Error: I don't have permission to edit channels. Try deleting the channels I create. Then, run the `setup` command again.")
|
||||||
|
await do_bot_cleanup(sid)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
await currServ.create_voice_channel(f"Players: {str(players)}/{str(max)}",
|
||||||
|
overwrites=overwrites)
|
||||||
|
|
||||||
|
do_show_hours = getShowHours(sid, conn)
|
||||||
|
if do_show_hours and not first_iter and lastSeconds != -1:
|
||||||
|
tStr = "Player Hrs: " + str(round(getPersonSeconds(sid, conn)/3600))
|
||||||
|
if len(tChannels) > 0:
|
||||||
|
lastTName = tChannels[0].name
|
||||||
|
|
||||||
|
if lastTName != tStr:
|
||||||
|
try:
|
||||||
|
print(currServ.name, "Update: Time changed!")
|
||||||
|
await tChannels[0].edit(name=tStr)
|
||||||
|
wait = 301
|
||||||
|
except discord.errors.Forbidden:
|
||||||
|
print(currServ.name,
|
||||||
|
"Error: I don't have permission to edit channels. Try deleting the channels I create. Then, run the `setup` command again.")
|
||||||
|
await do_bot_cleanup(sid)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
await currServ.create_voice_channel(tStr,
|
||||||
|
overwrites=overwrites)
|
||||||
|
elif not do_show_hours:
|
||||||
|
if len(tChannels) > 0:
|
||||||
|
for channel in tChannels:
|
||||||
|
await channel.delete()
|
||||||
|
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
first_iter = False
|
first_iter = False
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
PRAGMA journal_mode = WAL;
|
||||||
CREATE TABLE names (id INTEGER NOT NULL, name TEXT NOT NULL, UNIQUE (id, name));
|
CREATE TABLE names (id INTEGER NOT NULL, name TEXT NOT NULL, UNIQUE (id, name));
|
||||||
CREATE TABLE times (id INTEGER NOT NULL PRIMARY KEY, time NOT NULL DEFAULT 0) WITHOUT ROWID;
|
CREATE TABLE times (id INTEGER NOT NULL, name TEXT NOT NULL, seconds REAL NOT NULL DEFAULT 0, UNIQUE (id, name));
|
||||||
CREATE TABLE servers (id INTEGER PRIMARY KEY, ip TEXT NOT NULL, port INTEGER NOT NULL DEFAULT 25565, last_query TIMESTAMP, hours REAL NOT NULL DEFAULT 0, announce_joins BOOLEAN NOT NULL DEFAULT FALSE, announce_joins_id INTEGER) WITHOUT ROWID;
|
CREATE TABLE servers (id INTEGER PRIMARY KEY, ip TEXT NOT NULL, port INTEGER NOT NULL DEFAULT 25565, last_query TIMESTAMP, show_hours BOOLEAN NOT NULL DEFAULT FALSE, announce_joins BOOLEAN NOT NULL DEFAULT FALSE, announce_joins_id INTEGER) WITHOUT ROWID;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue