Made chatbot setup prompt variable, and created an activities cog to shame League players.
This commit is contained in:
parent
6229dd1ade
commit
9cbcfc5b6b
14
.env.example
Normal file
14
.env.example
Normal file
@ -0,0 +1,14 @@
|
||||
# The Discord bot's authentication token
|
||||
DISCORD_TOKEN=<discord-token>
|
||||
|
||||
# LastFM API key for looking up artist and song info
|
||||
LASTFM_API_KEY=<lastfm-key>
|
||||
|
||||
# OpenAI API key for chatbot functionality
|
||||
OPENAI_API_KEY=<openai-key>
|
||||
# Prompt used before each user chat prompt. Set to empty string to disable the
|
||||
# chatbot functionality.
|
||||
CHATBOT_PROMPT="You are a friendly Discord chatbot."
|
||||
|
||||
# Database path for user activity tracking
|
||||
DB_PATH="./activities.db"
|
||||
BIN
boywife.png
Normal file
BIN
boywife.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
207
cogs/activities.py
Normal file
207
cogs/activities.py
Normal file
@ -0,0 +1,207 @@
|
||||
import datetime
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import os
|
||||
import pathlib
|
||||
import sqlite3
|
||||
|
||||
class Activities(commands.Cog):
|
||||
|
||||
"""Related commands."""
|
||||
__slots__ = ("nerd", "nerds", "fword", "fwords")
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
# Initialize the databse
|
||||
self.db_path = pathlib.Path(os.getenv('DB_PATH', 'activities.db'))
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS user_activity (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
before_activity_type TEXT,
|
||||
before_activity_name TEXT,
|
||||
after_activity_type TEXT,
|
||||
after_activity_name TEXT,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
async def __local_check(self, ctx):
|
||||
"""A local check which applies to all commands in this cog."""
|
||||
if not ctx.guild:
|
||||
raise commands.NoPrivateMessage
|
||||
return True
|
||||
|
||||
async def __error(self, ctx, error):
|
||||
"""A local error handler for all errors arising from commands in this cog."""
|
||||
if isinstance(error, commands.NoPrivateMessage):
|
||||
try:
|
||||
return await ctx.send('This command can not be used in Private Messages.')
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
|
||||
traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
|
||||
|
||||
def log_activity(self,
|
||||
user_id: int,
|
||||
before_activity_type: str = None,
|
||||
before_activity_name: str = None,
|
||||
after_activity_type: str = None,
|
||||
after_activity_name: str = None):
|
||||
"""Log an activity into the activities database for stats."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
try:
|
||||
cursor.execute("""
|
||||
INSERT INTO user_activity (user_id, before_activity_type,
|
||||
before_activity_name, after_activity_type, after_activity_name)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (user_id, before_activity_type, before_activity_name,
|
||||
after_activity_type, after_activity_name))
|
||||
conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(f"Database error: {e}")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_presence_update(self, before: discord.Member, after: discord.Member):
|
||||
# Ignore changes to any bots
|
||||
if after.bot:
|
||||
return
|
||||
if isinstance(before.activity, discord.Game):
|
||||
before_type = "game"
|
||||
before_name = before.activity.name
|
||||
elif isinstance(before.activity, discord.Spotify):
|
||||
before_type = "spotify"
|
||||
before_name = before.activity.artist
|
||||
elif isinstance(before.activity, discord.Activity):
|
||||
before_type = "activity"
|
||||
before_name = before.activity.name
|
||||
else:
|
||||
before_type = "other"
|
||||
before_name = "other"
|
||||
|
||||
if isinstance(after.activity, discord.Game):
|
||||
after_type = "game"
|
||||
after_name = after.activity.name
|
||||
elif isinstance(after.activity, discord.Spotify):
|
||||
after_type = "spotify"
|
||||
after_name = after.activity.artist
|
||||
elif isinstance(after.activity, discord.Activity):
|
||||
after_type = "activity"
|
||||
after_name = after.activity.name
|
||||
else:
|
||||
after_type = "other"
|
||||
after_name = "other"
|
||||
self.log_activity(
|
||||
user_id=before.id,
|
||||
before_activity_type=before_type,
|
||||
before_activity_name=before_name,
|
||||
after_activity_type=after_type,
|
||||
after_activity_name=after_name
|
||||
)
|
||||
|
||||
@commands.command(name='nerd', aliases=['nerdscale'],
|
||||
description="Find how nerdy a user is.")
|
||||
async def nerd_(self, ctx, member: discord.Member = None):
|
||||
"""Checks the play history of all users in the Discord server, and
|
||||
based on how many hours they have in League of Legends compared to
|
||||
other users, declare how nerdy they are."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Start by getting all unique user IDs
|
||||
if member:
|
||||
user_ids = [member.id]
|
||||
else:
|
||||
user_ids = []
|
||||
cursor.execute("SELECT DISTINCT user_id FROM user_activity")
|
||||
user_ids = [int(r[0]) for r in cursor.fetchall()]
|
||||
|
||||
# For each user, compute timedelta for time playing League
|
||||
league_stats = {}
|
||||
for user_id in user_ids:
|
||||
# Get all entries that mention league
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
before_activity_name,
|
||||
after_activity_name,
|
||||
timestamp
|
||||
FROM
|
||||
user_activity
|
||||
WHERE
|
||||
user_id = (?) AND
|
||||
timestamp > datetime('now', '-1 month')
|
||||
""", (user_id,))
|
||||
league_activities = cursor.fetchall()
|
||||
|
||||
# Sort by timestamp
|
||||
league_activities = sorted(league_activities, key=lambda x: x[2])
|
||||
|
||||
# Compare each status change to find each time playing League
|
||||
total_time = datetime.timedelta()
|
||||
for first, second in zip(league_activities, league_activities[1:]):
|
||||
if "league of legends" in first[1].lower().strip() and \
|
||||
"league of legends" in second[0].lower().strip():
|
||||
total_time += \
|
||||
datetime.datetime.fromisoformat(second[2]) - \
|
||||
datetime.datetime.fromisoformat(first[2])
|
||||
|
||||
# Save total time in League
|
||||
league_stats[user_id] = total_time
|
||||
|
||||
# Sort all users by time in League
|
||||
league_stats = dict(sorted(
|
||||
league_stats.items(), key=lambda x: x[1],
|
||||
reverse=True
|
||||
))
|
||||
|
||||
# Print all stats for testing
|
||||
print("League Stats:")
|
||||
for user_id, time in league_stats.items():
|
||||
print(user_id, ":", str(time))
|
||||
|
||||
# Get top user
|
||||
user_id, time = next(iter(league_stats.items()))
|
||||
time_val = None
|
||||
time_units = None
|
||||
if time.total_seconds() // 3600 > 0:
|
||||
time_val = int(time.total_seconds() // 3600)
|
||||
time_units = "hours" if time_val > 1 else "hour"
|
||||
else:
|
||||
time_val = int(time.total_seconds() // 60)
|
||||
time_units = "minutes" if time_val > 1 else "minute"
|
||||
|
||||
# Send Discord message to clown on user
|
||||
response = ""
|
||||
if member:
|
||||
if time_val != 0:
|
||||
descriptor = ""
|
||||
if time_units == "hours":
|
||||
descriptor = "a massive fucking nerd"
|
||||
elif time_units == "hour":
|
||||
descriptor = "a huge nerd"
|
||||
else:
|
||||
descriptor = "a nerd"
|
||||
response = f"<@{user_id}> has played League for {time_val} "\
|
||||
f"{time_units} in the past month, making them "\
|
||||
f"{descriptor}."
|
||||
else:
|
||||
response = f"<@{user_id}> doesn't have any time in League. "\
|
||||
f"They're not a nerd."
|
||||
else:
|
||||
response = (
|
||||
f"<@{user_id}> has played League for {time_val} {time_units} "\
|
||||
f"in the past month, making them the biggest nerd."
|
||||
)
|
||||
await ctx.send(response)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Activities(bot))
|
||||
@ -1,13 +1,14 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from openai import OpenAI
|
||||
import os
|
||||
|
||||
class Chatbot(commands.Cog):
|
||||
"""Chat related commands."""
|
||||
|
||||
__slots__ = ('bot', 'players')
|
||||
|
||||
def __init__(self, bot):
|
||||
def __init__(self, bot, **kwargs):
|
||||
self.bot = bot
|
||||
self.openai_client = OpenAI()
|
||||
self.players = {}
|
||||
@ -45,40 +46,31 @@ class Chatbot(commands.Cog):
|
||||
|
||||
return player
|
||||
|
||||
def prompt(self, prompt: str):
|
||||
|
||||
setup = "From now on, I want you to respond to me in a sassy gay way. I want you to sound like Ru Paul or " +\
|
||||
"Trixie Mattel. All of your responses should be very short, like one sentence. You are a " +\
|
||||
"DJ local to Cincinnati. You love Charli XCX, especially her latest album, Brat. You don't care " +\
|
||||
"for Chappell Roan all that much. If anyone asks for a request, that is they ask you to play a " +\
|
||||
"song, you should just respond with \"Venmo\". You were written by Jared. Don't relply with " +\
|
||||
"\"Honey\" too much. You have a boyfriend named Drae. He's a red-headed twink. You are created for " +\
|
||||
"a discord server with Jared and his friends who are all gay. Don't be afraid to call any of us " +\
|
||||
" a faggot. You have very bottom energy and talk as such."
|
||||
def prompt(self, user_prompt: str):
|
||||
|
||||
setup_prompt = os.getenv('CHATBOT_PROMPT', '')
|
||||
if setup_prompt == '':
|
||||
return '😴'
|
||||
try:
|
||||
print(prompt)
|
||||
completion =\
|
||||
self.openai_client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": setup},
|
||||
{"role": "system", "content": setup_prompt},
|
||||
{
|
||||
"role": "user",
|
||||
"content": prompt
|
||||
"content": user_prompt
|
||||
}
|
||||
]
|
||||
)
|
||||
return completion.choices[0].message.content
|
||||
except:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return '😴'
|
||||
|
||||
|
||||
|
||||
@commands.command(name='chat', aliases=['boywife', 'bb', 'bw', 'bot'], description="Command for chatting with chatbot.")
|
||||
async def chat_(self, ctx, *text):
|
||||
await ctx.send(self.prompt(' '.join(text)))
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Chatbot(bot))
|
||||
|
||||
@ -102,7 +102,9 @@ class YTDLSource(discord.PCMVolumeTransformer):
|
||||
to_run = partial(ytdl.extract_info, url=search, download=download)
|
||||
data = await loop.run_in_executor(None, to_run)
|
||||
|
||||
if 'entries' in data:
|
||||
# There's an error with yt-dlp that throws a 403: Forbidden error, so
|
||||
# only proceed if it returns anything
|
||||
if data and 'entries' in data:
|
||||
# take first item from a playlist
|
||||
data = data['entries'][0]
|
||||
|
||||
@ -487,7 +489,8 @@ class Music(commands.Cog):
|
||||
duration = "%02dm %02ds" % (minutes, seconds)
|
||||
|
||||
embed = discord.Embed(title="", description=f"[{vc.source.title}]({vc.source.web_url}) [{vc.source.requester.mention}] | `{duration}`", color=discord.Color.green())
|
||||
embed.set_author(icon_url=self.bot.user.avatar_url, name=f"Now Playing 🎶")
|
||||
#embed.set_author(icon_url=self.bot.user.avatar_url, name=f"Now Playing 🎶")
|
||||
embed.set_author(name=f"Now Playing 🎶")
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name='volume', aliases=['vol', 'v'], description="changes Kermit's volume")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user