Quellcode durchsuchen

Quick & dirty refactor after discord.py upgrade (v2 required for buttons)

root vor 2 Jahren
Ursprung
Commit
18f88d7a37

+ 9 - 7
bot/commands/admin.py

@@ -5,10 +5,12 @@ from local_settings import WEB_SCHEME, WEB_HOST
 from query.guild_access_token import get_active_token, upsert_token
 from common.datetime import plus10min
 from common.logging import report
-from query.user import is_ignored
+from common.settings import check_ignore
+
+
+async def setup(bot: commands.Bot):
+	await bot.add_cog(Admin(bot))
 
-def setup(bot: commands.Bot):
-	bot.add_cog(Admin(bot))
 
 class Admin(commands.Cog):
 	"""Administrative functionality."""
@@ -24,16 +26,16 @@ class Admin(commands.Cog):
 	@commands.has_guild_permissions(administrator=True)
 	async def chanset(self, ctx: commands.Context):
 
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list.
+		if await check_ignore(self.bot.pg, ctx.author):
 			return
 
 		record = await get_active_token(self.bot.pg, ctx.guild.id)
 
-		if record:	# Check for active token
+		if record:  # Check for active token
 			await ctx.send(f"Token {record['id']} is in use by {record['user']} until {plus10min(record['created'])}.")
 			await report(self.bot, f"`{ctx.author}` has requested a token for `{ctx.guild.name}`, but got rejected until {plus10min(record['created'])} for use by `{record['user']}`.", ctx.guild)
-		else:	# No active token
+		else:  # No active token
 			token = secrets.token_urlsafe(40)[:40]
 			await upsert_token(self.bot.pg, ctx.guild.id, ctx.author.id, token)
 

+ 32 - 38
bot/commands/games.py

@@ -2,12 +2,14 @@ from discord.ext import commands
 import discord
 import random
 from typing import Optional
-from query.channel import get_games
-from query.user import is_ignored, get_level, get_xp, level_up, get_ability_points_spent, increment_all_coin, get_coin, get_karma
+from query.user import is_ignored, get_level, get_xp, level_up, get_ability_points_spent, increment_all_coin, get_coin, get_karma, get_theft_skill, get_random_player
 from local_settings import COMMAND_PREFIX
+from common.settings import check_ignore
+
+
+async def setup(bot: commands.Bot):
+	await bot.add_cog(Games(bot))
 
-def setup(bot: commands.Bot):
-	bot.add_cog(Games(bot))
 
 class Games(commands.Cog):
 	"""Gaming commands."""
@@ -15,20 +17,14 @@ class Games(commands.Cog):
 	def __init__(self, bot: commands.Bot):
 		self.bot = bot
 
-
 	@commands.command(
 		description="Check the level for a player.",
 		brief="Get player level",
 		help="View game level of player."
 	)
 	async def level(self, ctx: commands.Context, user: Optional[discord.User]):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
-			return
-
-		# Warn if games are off.
-		if not await get_games(self.bot.pg, ctx.channel.id):
-			ctx.author.send(f"Games are disabled in {ctx.channel}, ask an admin to enable them.")
+		# Halt on ignore list or games channel settings.
+		if await check_ignore(self.bot.pg, ctx.author, ctx.channel):
 			return
 
 		if not user:
@@ -56,13 +52,8 @@ class Games(commands.Cog):
 		help="View amount of XP a game player has."
 	)
 	async def xp(self, ctx: commands.Context, user: Optional[discord.User]):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
-			return
-
-		# Warn if games are off.
-		if not await get_games(self.bot.pg, ctx.channel.id):
-			ctx.author.send(f"Games are disabled in {ctx.channel}, ask an admin to enable them.")
+		# Halt on ignore list or games channel settings.
+		if await check_ignore(self.bot.pg, ctx.author, ctx.channel):
 			return
 
 		if not user:
@@ -88,13 +79,8 @@ class Games(commands.Cog):
 		help="Try to rank up a level in the game by spending XP."
 	)
 	async def levelup(self, ctx: commands.Context):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
-			return
-
-		# Warn if games are off.
-		if not await get_games(self.bot.pg, ctx.channel.id):
-			ctx.author.send(f"Games are disabled in {ctx.channel}, ask an admin to enable them.")
+		# Halt on ignore list or games channel settings.
+		if await check_ignore(self.bot.pg, ctx.author, ctx.channel):
 			return
 
 		xp_spent, total_xp = await get_xp(self.bot.pg, ctx.author.id)
@@ -108,6 +94,22 @@ class Games(commands.Cog):
 			await ctx.send(f"You have gained three ability points climbed the ranks for {threshold} XP, leaving you {xp_available - threshold} remaining.")
 			await increment_all_coin(self.bot.pg)
 
+	@commands.command(
+		description="Rob another player",
+		brief="Rob a player",
+		help="Pursuit a robbery."
+	)
+	async def rob(self, ctx: commands.Context):
+		# Halt on ignore list or games channel settings.
+		if await check_ignore(self.bot.pg, ctx.author, ctx.channel):
+			return
+
+		if get_theft_skill(self.bot.pg, ctx.author.id < 1):
+			await ctx.send("You do not have the `Theft` skill.")
+			return
+
+		victim_id = get_random_player(self.bot.pg)
+		await ctx.send(f"You have decided to rob{self.bot.get_user(victim_id)}, unfortunately crime has not been invited yet.")
 
 	@commands.command(
 		description="Simulate dice rolls.",
@@ -115,14 +117,10 @@ class Games(commands.Cog):
 		help="Roll two dice."
 	)
 	async def dice(self, ctx: commands.Context, amount: Optional[int], sides: Optional[int]):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list or games channel settings.
+		if await check_ignore(self.bot.pg, ctx.author, ctx.channel):
 			return
 
-		# Warn if games are off.
-		if not await get_games(self.bot.pg, ctx.channel.id):
-			await ctx.author.send(f"Games are disabled in {ctx.channel}, ask an admin to enable them.")
-			return
 		if not amount:
 			amount = 2
 		if not sides:
@@ -150,12 +148,8 @@ class Games(commands.Cog):
 		name="8ball"
 	)
 	async def eightball(self, ctx: commands.Context, *, question: str = None):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
-			return
-
-		if not await get_games(self.bot.pg, ctx.channel.id):
-			ctx.author.send(f"Games are disabled in {ctx.channel}, ask an admin to enable them.")
+		# Halt on ignore list or games channel settings.
+		if await check_ignore(self.bot.pg, ctx.author, ctx.channel):
 			return
 
 		if not question:

+ 22 - 22
bot/commands/general.py

@@ -5,26 +5,28 @@ from typing import Optional
 from query.channel import get_interact
 from query.user import ignore_user, unignore_user, is_ignored
 from common.logging import report
-from local_settings import COMMAND_PREFIX
+from local_settings import COMMAND_PREFIX, OUTPUT_CHANNEL
+from common.settings import check_ignore
 
-def setup(bot: commands.Bot):
-	bot.add_cog(General(bot))
 
-class General(commands.Cog):
+async def setup(bot: commands.Bot):
+	await bot.add_cog(GeneralCommands(bot))
+
+
+class GeneralCommands(commands.Cog):
 	"""General functionality."""
 
 	def __init__(self, bot: commands.Bot):
 		self.bot = bot
 
-
 	@commands.command(
 		description="Get the bot's current websocket and API latency.",
 		brief="Test latency",
 		help="Test latency by polling the gateway and API."
 	)
 	async def ping(self, ctx: commands.Context):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list.
+		if await check_ignore(self.bot.pg, ctx.author):
 			return
 
 		start_time = time.time()
@@ -39,8 +41,8 @@ class General(commands.Cog):
 		help="Display some crap."
 	)
 	async def info(self, ctx: commands.Context):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list.
+		if await check_ignore(self.bot.pg, ctx.author):
 			return
 
 		"""Displays some rubbish info."""
@@ -55,10 +57,11 @@ class General(commands.Cog):
 		aliases= ("say", "pm", "dm", "echo", "print")
 	)
 	async def msg(self, ctx: commands.Context, channel: Optional[discord.TextChannel], user: Optional[discord.User], *, message: str = None):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list.
+		if await check_ignore(self.bot.pg, ctx.author):
 			return
 
+		#print(ctx.author.permissions_in(self.bot.get_channel(OUTPUT_CHANNEL)).send_messages)
 		if not message:
 			if channel:
 				await ctx.send(f"What would you like me to say in `{channel}`?")
@@ -67,48 +70,45 @@ class General(commands.Cog):
 			else:
 				await ctx.send("What would you like me to say?")
 		elif channel:
-			if await get_interact(self.bot.pg, channel.id):
+			if await get_interact(self.bot.pg, channel.id) or ctx.author.permissions_in(self.bot.get_channel(OUTPUT_CHANNEL)).send_messages:
 				await channel.send(message)
 				await report(self.bot, f"`{ctx.author}` @ {channel.mention}: {message}", ctx.guild)
 			else:
 				await ctx.send(f"Interactive mode for {channel} is deactivated.")
-		elif user:
+		elif user and ctx.author.permissions_in(self.bot.get_channel(OUTPUT_CHANNEL)).send_messages:
 			await user.send(message)
 			await report(self.bot, f"`{ctx.author}`  has sent {message} to `{user.name}`")
 		else:
 			await ctx.send(message)
 			await report(self.bot, f"`{ctx.author}` has sent {message} locally.", ctx.guild)
 
-
 	@commands.command(
 		description="Change status.",
 		brief="Set status",
 		help="Update the bot's status."
 	)
 	async def status(self, ctx: commands.Context, *, text: str):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list.
+		if await check_ignore(self.bot.pg, ctx.author):
 			return
 
 		await self.bot.change_presence(activity=discord.Game(name=text))
 		await report(self.bot, f"`{ctx.author}` has set my status to `{text}`.")
 
-
 	@commands.command(
 		description="Get ignored.",
 		brief="Ignore sender",
 		help="Will have the bot ignore the user from now on."
 	)
 	async def ignoreme(self, ctx: commands.Context):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list.
+		if await check_ignore(self.bot.pg, ctx.author):
 			return
 
 		await ignore_user(self.bot.pg, ctx.author.id)
 		await ctx.send(f"To revert this use the `{COMMAND_PREFIX}unignoreme` command.")
 		await report(self.bot, f"`{ctx.author}` has requested to be ignored.")
 
-
 	@commands.command(
 		description="No longer get ingored.",
 		brief="Un-ignore sender",
@@ -125,8 +125,8 @@ class General(commands.Cog):
 		help="Verify if the user is being ignored."
 	)
 	async def isignored(self, ctx: commands.Context, user: Optional[discord.User]):
-		# Ignore user if on the ignore list.
-		if await is_ignored(self.bot.pg, ctx.author.id):
+		# Halt on ignore list.
+		if await check_ignore(self.bot.pg, ctx.author):
 			return
 
 		if not user:

+ 13 - 0
bot/common/settings.py

@@ -0,0 +1,13 @@
+from query.user import is_ignored
+from query.channel import get_games
+
+async def check_ignore(pg, user, games_channel=None):
+	# Ignore user if on the ignore list.
+	if await is_ignored(pg, user.id):
+		return True
+
+	# Ignore if games are disabled for channel.
+	if games_channel:  # Invoked by a game command.
+		if not await get_games(pg, games_channel.id):  # Warn if games are off.
+			user.send(f"Games are disabled in {games_channel}, ask an admin to enable them.")
+			return True

+ 5 - 8
bot/events/general.py

@@ -7,10 +7,10 @@ from query.channel_user import upsert_total_messages
 from query.user import create_user, created_invite, created_integration, member_updated, user_updated, member_banned, member_unbanned, presence_updated, message_edited, message_deleted, reacted, event_created, event_joined, thread_created, joined_thread, deleted_invite, member_joined, unreacted, event_parted, thread_deleted, left_thread
 from common.logging import report
 
-def setup(bot: commands.Bot):
-	bot.add_cog(General(bot))
+async def setup(bot: commands.Bot):
+	await bot.add_cog(GeneralEvents(bot))
 
-class General(commands.Cog):
+class GeneralEvents(commands.Cog):
 	"""A couple of simple commands."""
 
 	def __init__(self, bot: commands.Bot):
@@ -168,7 +168,7 @@ class General(commands.Cog):
 					"I'm not talking to you!",
 					"What have you been up to?",
 					"How is life?",
-	                "Kill all humans!",
+					"Kill all humans!",
 					f"{message.author.mention},What do you want from me?",
 					f"{message.author.mention}, do you care for me?",
 					f"{message.author.mention}, when will you stop talking about me?",
@@ -250,11 +250,8 @@ class General(commands.Cog):
 	async def on_thread_member_remove(self, member: discord.User):
 		left_thread(self.bot.pg, member.id)
 
-
-
-	# Undelete last deleted message
 	@commands.command(name="snipe")
-	async def snipe(self, ctx: commands.Context):
+	async def snipe(self, ctx: commands.Context):  # Undelete last deleted message
 		"""A command to snipe delete messages."""
 		if not self.last_deteled_msg:  # on_message_delete hasn't been triggered since the bot started
 			await ctx.send("There is no message to snipe!")

+ 64 - 62
bot/main.py

@@ -1,18 +1,19 @@
 import logging, os
+import discord
+from discord.ext import commands
+import asyncpg, asyncio
 
-from os.path import exists
 
-# Hint how to edit the settings and quit
-def hint_quit():
+def hint_quit():  # Hint how to edit the settings and quit
 	logging.info("")
 	logging.info("    edit local_settings.py")
 	logging.info("")
 	quit()
 
-# Copy or create settings file if missing
-def missing_config():
+
+def missing_config():  # Copy or create settings file if missing
 	logging.basicConfig(level=logging.DEBUG)
-	if not exists("local_settings.py"):
+	if not os.path.exists("local_settings.py"):
 		logging.error("Settings file not found.")
 		logging.info("Copying local_settings_example.py to local_settings.py")
 		try:
@@ -41,81 +42,82 @@ def missing_config():
 	logging.info("Configure the settings:")
 	hint_quit()
 
-# Hint to correct specific setting and quit
-def correct_setting(setting):
+
+def correct_setting(setting):  # Hint to correct specific setting and quit
 	logging.info("Correct the %s in local_settings.py", setting)
 	hint_quit()
 
-# Import settings
+
+# Attempt to import the local settings and quit gracefully on failure
 try:
-	import local_settings as settings   # Environment dependant settings stored in local_settings.py, untracked by .gitinore
-except ModuleNotFoundError:
-	missing_config()
+	import local_settings as settings  # Environment dependant settings stored in local_settings.py, untracked by .gitinore
+except ModuleNotFoundError:  # Local settings module import failure
+	missing_config()  # Prepare for configuration and inform operator
 
-# Check additional settings
-if not settings.WEB_HOST:
-	logging.error("Web host undefinded.")
-	correct_setting("WEB_HOST")
-	
-if not settings.WEB_SCHEME:
-	logging.error("Web scheme undefinded.")
-	correct_setting("WEB_SCHEME")
 
-# Set loglevel
-try:
-	logging.basicConfig(level=settings.LOG_LEVEL)
-except AttributeError:
-	missing_config()
+async def main():
+	# Check additional settings
+	if not settings.WEB_HOST:
+		logging.error("Web host undefinded.")
+		correct_setting("WEB_HOST")
+	if not settings.WEB_SCHEME:
+		logging.error("Web scheme undefinded.")
+		correct_setting("WEB_SCHEME")
+
+	# Set loglevel
+	try:
+		logging.basicConfig(level=settings.LOG_LEVEL)
+	except AttributeError:
+		missing_config()
 
-# Define database pool
-import asyncpg
-async def create_db_pool():
+	# Define robot
+	intents = discord.Intents.default()
+	intents.message_content = True
 	try:
-		bot.pg = await asyncpg.create_pool(
-			database=settings.DATABASE_NAME,
-			user=settings.DATABASE_USER,
-			host=settings.DATABASE_HOST,
-			password=settings.DATABASE_PASSWORD,
+		bot = commands.Bot(
+			command_prefix=settings.COMMAND_PREFIX,
+			description="Charlie's Angels bot",
+			intents=intents,
+			case_insensitive=True,
 		)
 	except AttributeError:
 		missing_config()
 
-# Create robot
-import discord
-from discord.ext import commands
-try:
-	bot = commands.Bot(
-		command_prefix = settings.COMMAND_PREFIX,
-		description = "Charlie's Angels bot",
-		intents = discord.Intents.default(),    # Required: Guilds
-		case_insensitive = True,
-	)
-except AttributeError:
-	print("Attribute error on create.")
-	missing_config()
+	# Load extensions
+	default_extensions = [
+		"commands.admin",
+		"commands.games",
+		"commands.general",
+		"events.general",
+	]
+	for ext in default_extensions:
+		logging.info(f"Loading extension {ext}")
+		await bot.load_extension(ext)
+
+	async def create_db_pool():	 # Connect to database
+		try:
+			bot.pg = await asyncpg.create_pool(
+				database=settings.DATABASE_NAME,
+				user=settings.DATABASE_USER,
+				host=settings.DATABASE_HOST,
+				password=settings.DATABASE_PASSWORD,
+			)
+		except AttributeError:
+			missing_config()
 
-# Create database pool
-bot.loop.run_until_complete(create_db_pool())
+	# Create database pool
+	await create_db_pool()
 
-# Create database tables if they do not exist
-from query.initialise_database import init_db
-bot.loop.run_until_complete(init_db(bot.pg))
+	# Create database tables if they do not exist
+	from query.initialise_database import init_db
+	await init_db(bot.pg)
+	await bot.start(settings.DISCORD_TOKEN)
 
-# Load extensions
-default_extensions = [
-	"commands.admin",
-	"commands.games",
-	"commands.general",
-	"events.general",
-]
-for ext in default_extensions:
-  bot.load_extension(ext)
 
 # Run robot
 try:
-	bot.run(settings.DISCORD_TOKEN)
+	asyncio.run(main())
 except AttributeError:
-	print("Attribute error on run.")
 	missing_config()
 except discord.errors.LoginFailure:
 	logging.error("Invalid discord token.")

+ 5 - 0
bot/query/initialise_database.py

@@ -55,6 +55,11 @@ async def init_db(pg):
                 ability_points_spent INT DEFAULT 0, \
                 coin INT DEFAULT 0, \
                 karma INT DEFAULT 0, \
+                defence_skill INT DEFAULT 0, \
+                attack_skill  INT DEFAULT 0, \
+                stealth_skill INT DEFAULT 0, \
+                perception_skill INT DEFAULT 0, \
+                theft_skill INT DEFAULT 0, \
                 created TIMESTAMP NOT NULL DEFAULT now()\
             )\
         ",

+ 39 - 1
bot/query/user.py

@@ -1,18 +1,23 @@
 async def create_user(pg, user_id):
 	await pg.execute("INSERT INTO \"user\"(user_id) VALUES($1) ON CONFLICT DO NOTHING", user_id)
 
+
 async def ignore_user(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET ignore = TRUE WHERE user_id = $1", user_id)
 
+
 async def unignore_user(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET ignore = FALSE WHERE user_id = $1", user_id)
 
+
 async def is_ignored(pg, user_id):
 	return await pg.fetchval("SELECT ignore FROM \"user\" WHERE user_id = $1", user_id)
 
+
 async def get_level(pg, user_id):
 	return await pg.fetchval("SELECT level FROM \"user\" WHERE user_id = $1", user_id)
 
+
 async def get_xp(pg, user_id):
 	message_array = await pg.fetch("SELECT total_messages FROM channel_user WHERE \"user\" = $1", user_id)
 	total_xp = 0
@@ -45,77 +50,110 @@ async def get_xp(pg, user_id):
 async def level_up(pg, user_id, xp):
 	await pg.execute("UPDATE \"user\" SET (level, xp_spent) = (+ 1, $2) WHERE user_id = $1", user_id, xp)
 
+
 async def created_invite(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (invites_created, karma) = (+1, +1) WHERE user_id = $1", user_id)
 
+
 async def deleted_invite(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET karma = -1 WHERE user_id = $1", user_id)
 
+
 async def invites_created(pg, user_id):
 	return await pg.fetchval("SELECT invites_created FROM \"user\" WHERE user_id = $1", user_id)
 
+
 async def created_integration(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET integrations_created = +1 WHERE user_id = $1", user_id)
 
+
 async def member_joined(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET karma = +1 WHERE user_id = $1", user_id)
 
+
 async def member_updated(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET member_updated = +1 WHERE user_id = $1", user_id)
 
+
 async def user_updated(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET user_updated = +1 WHERE user_id = $1", user_id)
 
+
 async def member_banned(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (member_banned, karma) = (+1, -1) WHERE user_id = $1", user_id)
 
+
 async def member_unbanned(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET member_unbanned = +1 WHERE user_id = $1", user_id)
 
+
 async def presence_updated(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET presence_updated = +1 WHERE user_id = $1", user_id)
 
+
 async def message_edited(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (messages_edited, karma) = (+1, +1) WHERE user_id = $1", user_id)
 
+
 async def message_deleted(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (messages_deleted, karma) = (+1, -1) WHERE user_id = $1", user_id)
 
+
 async def reacted(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (reacted, karma) = (+1, +1) WHERE user_id = $1", user_id)
 
+
 async def unreacted(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET karma = -1 WHERE user_id = $1", user_id)
 
+
 async def event_created(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (events_created, karma) = (+1, +1) WHERE user_id = $1", user_id)
 
+
 async def event_joined(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (events_joined, karma) = (+1, +1) WHERE user_id = $1", user_id)
 
+
 async def event_parted(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET karma = -1 WHERE user_id = $1", user_id)
 
+
 async def thread_created(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (threads_created, karma) = (+1, +1) WHERE user_id = $1", user_id)
 
+
 async def thread_deleted(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET karma = - 1 WHERE user_id = $1", user_id)
 
+
 async def joined_thread(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET (threads_joined, karma) = (+1, +1) WHERE user_id = $1", user_id)
 
+
 async def left_thread(pg, user_id):
 	await pg.execute("UPDATE \"user\" SET karma = -1 WHERE user_id = $1", user_id)
 
+
 async def get_ability_points_spent(pg, user_id):
 	return await pg.fetchval("SELECT ability_points_spent FROM \"user\" WHERE user_id = $1", user_id)
 
+
 async def increment_all_coin(pg):
 	await pg.execute("UPDATE \"user\" SET coin = + 1 WHERE level > 0")
 
+
 async def get_coin(pg, user_id):
 	return await pg.fetchval("SELECT coin FROM \"user\" WHERE user_id = $1", user_id)
 
+
 async def get_karma(pg, user_id):
-	return await pg.fetchval("SELECT karma FROM \"user\" WHERE user_id = $1", user_id)
+	return await pg.fetchval("SELECT karma FROM \"user\" WHERE user_id = $1", user_id)
+
+
+async def get_theft_skill(pg, user_id):
+	return await pg.fetchval("SELECT theft_skill FROM \"user\" WHERE user_id = $1", user_id)
+
+
+async def get_random_player(pg):
+	return await pg.fetchval("SELECT user_id FROM \"user\" ORDER BY radoom() LIMIT 1")

+ 1 - 1
requirements.txt

@@ -1,4 +1,4 @@
-discord.py==1.7.3
+discord.py==2.1.0
 asyncpg==0.26.0
 Django==4.1
 psycopg2-binary==2.9.5