Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f51afc134a | |||
| aa15d5c026 | |||
| 5721939412 | |||
|
|
d194332843 | ||
| fa2e4a78cd | |||
|
|
27c9dd353f | ||
| ab1fca82dc | |||
| ceed2ad105 | |||
| 9fd6701f53 | |||
|
|
c8c33e6f25 | ||
|
|
1fc1ac5feb | ||
| c565e41a4b | |||
| 9b3642c92b | |||
| eb7d4de9e1 | |||
|
|
bf37ba09b6 | ||
| c72aa1df67 | |||
|
|
e7f39ac82b | ||
|
|
83a97aeef0 | ||
| 337f4abfcd | |||
| a1b47248e3 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -3,7 +3,7 @@
|
||||
github: #
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: parkel
|
||||
ko_fi: pvanarkel
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,9 +1,9 @@
|
||||
jeevesbot/env.py
|
||||
jeevesbot/secret.json
|
||||
jeevesbot/databases/*
|
||||
jeevesbot/__pycache__/*
|
||||
logs/
|
||||
__init__.py
|
||||
*.pyc
|
||||
__pycache__/*
|
||||
cogs/__pycache__/*
|
||||
jeevesbot/__pycache__/*
|
||||
.vscode/
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 - Peter van Arkel
|
||||
Copyright (c) 2024 - Peter van Arkel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
18
Makefile
18
Makefile
@@ -1,18 +0,0 @@
|
||||
init:
|
||||
pip install -r requirements.txt
|
||||
|
||||
install:
|
||||
mkdir logs
|
||||
cp jeevesbot/env.py.dist jeevesbot/env.py
|
||||
cp jeevesbot/secret.json.dist jeevesbot/secret.json
|
||||
|
||||
clearlog:
|
||||
rm logs/jeeves.log*
|
||||
|
||||
clean:
|
||||
rm logs/jeeves.log*
|
||||
rm jeevesbot/env.py
|
||||
rm jeevesbot/secret.json
|
||||
rm -rf cogs/__pycache__/
|
||||
rm -rf jeevesbot/__pycache__/
|
||||
rm -rf __pycache__/
|
||||
@@ -1 +1,9 @@
|
||||
# discord-jeeves
|
||||
|
||||
## Installation
|
||||
- Clone this repository to a directory you want the bot to run from
|
||||
- `pip3 install -r requirements.txt`
|
||||
- Copy `jeevesbot/env.py.dist` to `jeevesbot/env.py` and change the variables.
|
||||
- `cp scripts/jeeves.service /etc/systemd/system/jeeves.service` and change the variables to suit your environment.
|
||||
- `systemctl daemon-reload`
|
||||
- `systemctl start jeeves.service`
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from logging import getLogger
|
||||
import typing
|
||||
@@ -9,9 +8,6 @@ from discord import User, errors, TextChannel, Forbidden
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
embed = discord.Embed()
|
||||
|
||||
|
||||
class Admin(commands.Cog):
|
||||
""" Admin Commands, Use at own risk. """
|
||||
def __init__(self, bot):
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from logging import getLogger
|
||||
from jeevesbot import env
|
||||
|
||||
# setup logging
|
||||
log = getLogger(__name__)
|
||||
e = discord.Embed()
|
||||
|
||||
|
||||
class Flair(commands.Cog):
|
||||
""" This part of the bot is responsible for giving users roles! """
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.message_id = 1084937456819908679
|
||||
self.reactions = {
|
||||
'1️⃣' : env.FLAIRROLES[0],
|
||||
'2️⃣' : env.FLAIRROLES[1],
|
||||
'3️⃣' : env.FLAIRROLES[2],
|
||||
'4️⃣' : env.FLAIRROLES[3],
|
||||
'5️⃣' : env.FLAIRROLES[4],
|
||||
'6️⃣' : env.FLAIRROLES[5],
|
||||
'👍' : env.FLAIRROLES[6],
|
||||
'👎' : env.FLAIRROLES[7],
|
||||
'🔞' : env.FLAIRROLES[8],
|
||||
}
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_add(self, payload):
|
||||
try:
|
||||
if payload.message_id == self.message_id and str(payload.emoji) in self.reactions:
|
||||
guild = self.bot.get_guild(payload.guild_id)
|
||||
member = guild.get_member(payload.user_id)
|
||||
role_id = self.reactions[str(payload.emoji)]
|
||||
role = guild.get_role(role_id)
|
||||
flairrole = guild.get_role(env.FLAIRROLE[0])
|
||||
await member.add_roles(flairrole)
|
||||
await member.add_roles(role)
|
||||
log.info(f'Added role "{role}" to {member}')
|
||||
except:
|
||||
log.debug("Reaction not found.")
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_remove(self, payload):
|
||||
try:
|
||||
if payload.message_id == self.message_id and str(payload.emoji) in self.reactions:
|
||||
guild = self.bot.get_guild(payload.guild_id)
|
||||
member = guild.get_member(payload.user_id)
|
||||
role_id = self.reactions[str(payload.emoji)]
|
||||
role = guild.get_role(role_id)
|
||||
await member.remove_roles(role)
|
||||
log.info(f'Removed role "{role}" from {member}')
|
||||
except:
|
||||
log.debug("Could not remove role")
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
log.info(f'module active')
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Flair(bot))
|
||||
@@ -1,6 +1,6 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from jeevesbot import functions, babbelbingo
|
||||
from jeevesbot import functions
|
||||
from logging import getLogger
|
||||
|
||||
|
||||
@@ -17,15 +17,6 @@ class Games(commands.Cog):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
async def bingo(self, ctx):
|
||||
""" Maak een babbelbingo kaart (staat niet altijd aan) """
|
||||
name = ctx.message.author.name
|
||||
bingocard = babbelbingo.bingo(name)
|
||||
await ctx.author.send(file=discord.File(bingocard))
|
||||
|
||||
|
||||
@commands.command()
|
||||
async def roll(self, ctx):
|
||||
""" Roll the dice!
|
||||
|
||||
66
cogs/gif.py
66
cogs/gif.py
@@ -1,66 +0,0 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from jeevesbot import functions
|
||||
from logging import getLogger
|
||||
|
||||
|
||||
# setup logging
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
e = discord.Embed()
|
||||
|
||||
|
||||
class Gif(commands.Cog):
|
||||
""" Ensures that high-risk channels don't display embedded links, but only gifs."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.content.startswith('https://tenor.com/'):
|
||||
roles = functions.checkrole(message.author.roles)
|
||||
channel = functions.checkchannel(message.channel.id)
|
||||
embed_url = message.content
|
||||
follow_url = embed_url + '.gif'
|
||||
full_url = await functions.resolve(follow_url)
|
||||
gif_url = full_url.split('?')[0]
|
||||
embed = e.set_image(url=gif_url)
|
||||
if channel is True:
|
||||
if roles is not True:
|
||||
await message.channel.send(embed=embed)
|
||||
logline = (str(message.author) + ' requested a gif: ' + str(gif_url))
|
||||
log.info(logline)
|
||||
if message.content.endswith('.gif'):
|
||||
roles = functions.checkrole(message.author.roles)
|
||||
channel = functions.checkchannel(message.channel.id)
|
||||
embed_url = message.content
|
||||
embed = e.set_image(url=embed_url)
|
||||
if channel is True:
|
||||
if roles is not True:
|
||||
await message.channel.send(embed=embed)
|
||||
logline = (str(message.author) + ' requested a gif: ' + str(embed_url))
|
||||
log.info(logline)
|
||||
if message.content.startswith('https://giphy.com/'):
|
||||
roles = functions.checkrole(message.author.roles)
|
||||
channel = functions.checkchannel(message.channel.id)
|
||||
embed_url = message.content
|
||||
image_code = embed_url.split('-')[-1]
|
||||
gif_url = 'https://media.giphy.com/media/' + image_code + '/giphy.gif'
|
||||
embed = e.set_image(url=gif_url)
|
||||
if channel is True:
|
||||
if roles is not True:
|
||||
await message.channel.send(embed=embed)
|
||||
logline = (str(message.author) + ' requested a gif: ' + str(gif_url))
|
||||
log.info(logline)
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
log.info(f'module active')
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Gif(bot))
|
||||
@@ -2,11 +2,9 @@ import discord
|
||||
from discord.ext import commands
|
||||
from logging import getLogger
|
||||
|
||||
|
||||
# setup logging
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
e = discord.Embed()
|
||||
|
||||
|
||||
@@ -17,12 +15,6 @@ class Misc(commands.Cog):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
@commands.command()
|
||||
async def hug(self, ctx):
|
||||
msg = f'Jeeves geeft {ctx.message.author.mention} een grote knuffel'
|
||||
await ctx.send(msg)
|
||||
|
||||
|
||||
@commands.command()
|
||||
async def eirik(self, ctx):
|
||||
await ctx.send('Deze vraag heeft Eirik Fatland al beantwoord in 1997.')
|
||||
|
||||
107
cogs/preview.py
Normal file
107
cogs/preview.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from jeevesbot import functions, env
|
||||
from logging import getLogger
|
||||
import re
|
||||
import pytube
|
||||
from pytube.exceptions import RegexMatchError, VideoUnavailable, ExtractError
|
||||
|
||||
# setup logging
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
class Preview(commands.Cog):
|
||||
""" Ensures that high-risk channels don't display embedded links, but only gifs and youtube previews."""
|
||||
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.video_id_regex = re.compile(r'(?:youtube\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/|m\.youtube\.com/(?:watch\?v=|embed/|v/))([^"&?/ ]{11})')
|
||||
self.e = discord.Embed()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.bot:
|
||||
return # Ignore messages from bots
|
||||
|
||||
|
||||
if message.content.startswith('https://tenor.com/'):
|
||||
is_admin = message.author.guild_permissions.administrator
|
||||
is_high_risk_channel = self.check_channel(message.channel.id)
|
||||
embed_url = message.content
|
||||
follow_url = embed_url + '.gif'
|
||||
full_url = await functions.resolve(follow_url)
|
||||
gif_url = full_url.split('?')[0]
|
||||
embed = self.e.set_image(url=gif_url)
|
||||
if is_high_risk_channel and not is_admin:
|
||||
await message.channel.send(embed=embed)
|
||||
logline = (str(message.author) + ' requested a gif: ' + str(gif_url))
|
||||
log.info(logline)
|
||||
|
||||
|
||||
if message.content.startswith('https://giphy.com/'):
|
||||
is_admin = message.author.guild_permissions.administrator
|
||||
is_high_risk_channel = self.check_channel(message.channel.id)
|
||||
embed_url = message.content
|
||||
image_code = embed_url.split('-')[-1]
|
||||
gif_url = 'https://media.giphy.com/media/' + image_code + '/giphy.gif'
|
||||
embed = self.e.set_image(url=gif_url)
|
||||
if is_high_risk_channel and not is_admin:
|
||||
await message.channel.send(embed=embed)
|
||||
logline = (str(message.author) + ' requested a gif: ' + str(gif_url))
|
||||
log.info(logline)
|
||||
|
||||
|
||||
if message.content.endswith('.gif'):
|
||||
is_admin = message.author.guild_permissions.administrator
|
||||
is_high_risk_channel = self.check_channel(message.channel.id)
|
||||
embed_url = message.content
|
||||
embed = self.e.set_image(url=embed_url)
|
||||
if is_high_risk_channel and not is_admin:
|
||||
await message.channel.send(embed=embed)
|
||||
logline = (str(message.author) + ' requested a gif: ' + str(embed_url))
|
||||
log.info(logline)
|
||||
|
||||
|
||||
if 'https://youtu' in message.content or 'https://m.youtu' in message.content or 'https://www.youtu' in message.content:
|
||||
is_admin = message.author.guild_permissions.administrator
|
||||
is_high_risk_channel = self.check_channel(message.channel.id)
|
||||
if is_high_risk_channel and not is_admin:
|
||||
url = message.content
|
||||
try:
|
||||
youtube = pytube.YouTube(url)
|
||||
video_title = youtube.title
|
||||
video_author = youtube.author
|
||||
video_id = self.extract_video_id(url)
|
||||
if video_id:
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg")
|
||||
embed.set_author(name=video_author)
|
||||
embed.add_field(name="", value=f"[{video_title}]({url})")
|
||||
await message.channel.send(embed=embed)
|
||||
log.info(f'User {message.author} requested preview for {url}')
|
||||
except (VideoUnavailable, ExtractError, KeyError) as e:
|
||||
log.error(f'Error extracting YouTube video details for {url}: {e}')
|
||||
await message.channel.send('Sorry, there was an error retrieving the YouTube video details.')
|
||||
|
||||
|
||||
def check_channel(self, channel_id):
|
||||
high_risk_channels = env.PREVIEWCHANNELS
|
||||
return channel_id in high_risk_channels
|
||||
|
||||
def extract_video_id(self, url):
|
||||
try:
|
||||
match = self.video_id_regex.search(url)
|
||||
if match:
|
||||
return match.group(1)
|
||||
except pytube.exceptions.RegexMatchError as e:
|
||||
pass
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
log.info(f'module active')
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Preview(bot))
|
||||
41
cogs/reminders.py
Normal file
41
cogs/reminders.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from jeevesbot import env
|
||||
import datetime
|
||||
from jeevesbot.database import add_reminder
|
||||
from logging import getLogger
|
||||
|
||||
|
||||
# setup logging
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
class Reminders(commands.Cog):
|
||||
""" Reminder command"""
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
@discord.app_commands.command(name='remindme', description='Set a reminder - Use YY-MM-DD HH:MM:SS notation')
|
||||
@discord.app_commands.guilds(discord.Object(id=env.GUILD_ID))
|
||||
async def remindme(self, interaction: discord.Interaction, time: str, message: str):
|
||||
try:
|
||||
reminder_time = datetime.datetime.strptime(time, '%Y-%m-%d %H:%M:%S')
|
||||
add_reminder(interaction.user.id, message, reminder_time.isoformat())
|
||||
await interaction.response.send_message(f'Reminder set for {reminder_time}')
|
||||
log.info(f'Reminder set by {interaction.user} for {reminder_time}: {message}')
|
||||
except ValueError:
|
||||
await interaction.response.send_message('Invalid time format. Use YYYY-MM-DD HH:MM:SS', ephemeral=True)
|
||||
log.warn(f'Reminder set by {interaction.user} went wrong.')
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
log.info(f'module active')
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Reminders(bot))
|
||||
log.info(f'Added Reminders.remindme as command')
|
||||
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from logging import getLogger
|
||||
from jeevesbot import env
|
||||
|
||||
|
||||
# setup logging
|
||||
log = getLogger(__name__)
|
||||
|
||||
embed = discord.Embed()
|
||||
|
||||
class BotSetup(commands.Cog):
|
||||
""" Admin Commands, Use at own risk. """
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.botchannel = int(env.BOTCHANNEL[0])
|
||||
|
||||
|
||||
@commands.command(hidden=True)
|
||||
@commands.is_owner()
|
||||
async def flairsetup(self, ctx):
|
||||
""" Setup reaction post as embed. """
|
||||
await ctx.message.delete()
|
||||
channel_id = ctx.channel.id
|
||||
if (channel_id) == (self.botchannel):
|
||||
embed = discord.Embed(title="Kies je rollen!",
|
||||
description="Klik op de emoji onder het bericht om de rol te krijgen.")
|
||||
embed.add_field(name="", value="", inline=False)
|
||||
embed.add_field(name="Voornaamwoorden:", value="", inline=False)
|
||||
embed.add_field(name=":one:", value="hij/hem")
|
||||
embed.add_field(name=":two:", value="zij/haar")
|
||||
embed.add_field(name=":three:", value="hen/hun")
|
||||
embed.add_field(name=":four:", value="die/hun")
|
||||
embed.add_field(name=":five:", value="die/diens")
|
||||
embed.add_field(name=":six:", value="iedere/all")
|
||||
embed.add_field(name="Sta je open voor Direct Messages van andere serverleden?", value="", inline=False)
|
||||
embed.add_field(name=":thumbsup:", value="DM: ja")
|
||||
embed.add_field(name=":thumbsdown:", value="DM: nee")
|
||||
embed.add_field(name="Rollen voor toegang tot opt-in kanalen.", value="", inline=False)
|
||||
embed.add_field(name=":underage:", value="serieuze-onderwerpen")
|
||||
embed.set_footer(text="Mis je een voornaamwoord of heb je ideeën voor een andere rol? Laat het de mods weten in de #ideeënbus!\nJe kan zelf je rollen verwijderen door opnieuw op de emoji te drukken.")
|
||||
message = await ctx.send(embed=embed)
|
||||
await message.add_reaction("1️⃣")
|
||||
await message.add_reaction("2️⃣")
|
||||
await message.add_reaction("3️⃣")
|
||||
await message.add_reaction("4️⃣")
|
||||
await message.add_reaction("5️⃣")
|
||||
await message.add_reaction("6️⃣")
|
||||
await message.add_reaction("👍")
|
||||
await message.add_reaction("👎")
|
||||
await message.add_reaction("🔞")
|
||||
log.warn(f'{ctx.message.author} reset the flair embed, new message_id = {message.id}')
|
||||
else:
|
||||
log.warn(f'{ctx.message.author} tried setting up the flair in the wrong channel')
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
log.info(f'module active')
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(BotSetup(bot))
|
||||
@@ -14,9 +14,4 @@ All purge commands also delete their own message.
|
||||
|
||||
---
|
||||
|
||||
`!flairsetup`
|
||||
Can be used to reset the flair embed for Jeeves. Needs manual steps to fix the bot giving out roles. Don't use unless you know what you're doing.
|
||||
|
||||
---
|
||||
|
||||
*To easily copy IDs of discord items, turn on Developer Mode in Advanced settings in your profile. After this, you can right click on almost all items in discord to get the "Copy ID" option.*
|
||||
@@ -3,9 +3,6 @@
|
||||
`!roll <xdx>`
|
||||
Rolt dobbelstenen voor je! De standaard notatie is xdx, maar ook ingewikkelde rolls zijn mogelijk. Zie `!info` voor meer informatie.
|
||||
|
||||
`!bingo`
|
||||
Maakt een Babbelbingo kaart voor je aan. Deze optie wordt doorgaans alleen aangezet tijdens digitale evenementen.
|
||||
|
||||
** Links **
|
||||
`!youtube`
|
||||
`!donatie`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
** GIFs **
|
||||
** Flair **
|
||||
Om te voorkomen dat mensen een linkje posten die direct een confronterende preview kan geven, staan embeds uit in sommige kanalen waar daar meer kans op is.
|
||||
Dit zorgt er helaas wel voor dat GIFjes in die kanalen niet werken. Door Jeeves is het toch mogelijk om het GIFje te laten zien, zonder dat confronterende previews van links er ook zijn.
|
||||
|
||||
** Extra informatie voor het !roll commando **
|
||||
|
||||
|
||||
90
jeeves.py
90
jeeves.py
@@ -1,66 +1,108 @@
|
||||
#!/usr/bin/env python3.9
|
||||
#!/usr/bin/env python3.8
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ext import commands, tasks
|
||||
from jeevesbot import env
|
||||
from jeevesbot.database import init_db, get_due_reminders
|
||||
import os
|
||||
import log
|
||||
import logging.config
|
||||
from logging import getLogger
|
||||
import datetime
|
||||
import asyncio
|
||||
|
||||
|
||||
# setup root logger handlers
|
||||
logging.config.dictConfig(log.LOGGING)
|
||||
# Initialize the database
|
||||
init_db()
|
||||
|
||||
|
||||
# setup logging
|
||||
logging.config.dictConfig(log.LOGGING)
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
# setup discord.py bot
|
||||
intents = discord.Intents().all()
|
||||
bot = commands.Bot(command_prefix='!', intents=intents, help_command=None)
|
||||
intents.message_content = True
|
||||
e = discord.Embed()
|
||||
|
||||
class Jeeves(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix='!', intents=intents, help_command=None)
|
||||
self.guild_ids = [env.GUILD_ID]
|
||||
|
||||
@bot.command(name='load', hidden=True)
|
||||
|
||||
@commands.command(name='load', hidden=True)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def load(ctx, extension):
|
||||
bot.load_extension(f'cogs.{extension}')
|
||||
async def load(self, ctx, extension):
|
||||
self.load_extension(f'cogs.{extension}')
|
||||
log.info(f'{ctx.message.author} loaded the {extension} module')
|
||||
|
||||
|
||||
@bot.command(name='unload', hidden=True)
|
||||
@commands.command(name='unload', hidden=True)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def unload(ctx, extension):
|
||||
bot.unload_extension(f'cogs.{extension}')
|
||||
async def unload(self, ctx, extension):
|
||||
self.unload_extension(f'cogs.{extension}')
|
||||
log.info(f'{ctx.message.author} unloaded the {extension} module')
|
||||
|
||||
|
||||
@bot.command(name='reload', hidden=True)
|
||||
@commands.command(name='reload', hidden=True)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def reload(ctx, extension):
|
||||
bot.unload_extension(f'cogs.{extension}')
|
||||
bot.load_extension(f'cogs.{extension}')
|
||||
async def reload(self, ctx, extension):
|
||||
self.unload_extension(f'cogs.{extension}')
|
||||
self.load_extension(f'cogs.{extension}')
|
||||
log.info(f'{ctx.message.author} reloaded the {extension} module')
|
||||
|
||||
|
||||
async def load_extensions():
|
||||
async def load_extensions(self):
|
||||
for filename in os.listdir('./cogs'):
|
||||
if filename.endswith('.py'):
|
||||
await bot.load_extension(f'cogs.{filename[:-3]}')
|
||||
await self.load_extension(f'cogs.{filename[:-3]}')
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
log.info(f'Active with ID:{bot.user.id} as {bot.user.name}')
|
||||
@tasks.loop(seconds=60)
|
||||
async def check_reminders(self):
|
||||
now = datetime.datetime.now().isoformat()
|
||||
reminders = get_due_reminders(now)
|
||||
for reminder in reminders:
|
||||
user = self.get_user(reminder[1])
|
||||
if user:
|
||||
try:
|
||||
await user.send(reminder[2])
|
||||
except Exception as e:
|
||||
log.error(f'Error sending reminder to user {reminder[1]}: {e}')
|
||||
|
||||
|
||||
async def on_ready(self):
|
||||
log.info(f'Active with ID:{self.user.id} as {self.user.name}')
|
||||
activity = discord.Activity(name='!help', type=discord.ActivityType.listening)
|
||||
await bot.change_presence(activity=activity)
|
||||
await self.change_presence(activity=activity)
|
||||
# Sync commands for all guilds
|
||||
for guild_id in self.guild_ids:
|
||||
guild = discord.Object(id=guild_id)
|
||||
try:
|
||||
await self.tree.sync(guild=guild)
|
||||
log.info(f'Successfully synced commands for guild {guild_id}')
|
||||
except discord.errors.Forbidden as e:
|
||||
log.error(f'Failed to sync commands for guild {guild_id}: {e}')
|
||||
# Start the reminder check loop
|
||||
if not self.check_reminders.is_running():
|
||||
self.check_reminders.start()
|
||||
|
||||
|
||||
async def on_command_error(self, ctx, error):
|
||||
if isinstance(error, commands.CommandNotFound):
|
||||
await ctx.send('Command not found.')
|
||||
log.warning(f'Command not found: {ctx.message.content}')
|
||||
else:
|
||||
await ctx.send('An error occurred.')
|
||||
log.error(f'An error occurred: {error}')
|
||||
|
||||
|
||||
async def main():
|
||||
async with bot:
|
||||
await load_extensions()
|
||||
bot = Jeeves()
|
||||
await bot.load_extensions()
|
||||
await bot.start(env.TOKEN)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,84 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import gspread
|
||||
from oauth2client.service_account import ServiceAccountCredentials
|
||||
import random
|
||||
from PIL import Image, ImageFont, ImageDraw
|
||||
import textwrap
|
||||
|
||||
# setup gspread
|
||||
scope = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive']
|
||||
creds = ServiceAccountCredentials.from_json_keyfile_name('jeevesbot/secret.json', scope)
|
||||
gclient = gspread.authorize(creds)
|
||||
|
||||
# load babbelbingofile
|
||||
def babbelbingo_file():
|
||||
file = gclient.open_by_key("1zdWl17fhdT2P96ZgjSwB_2wsCHi_ZVA42JroX5d1ylc")
|
||||
babbelbingo = file.get_worksheet(1)
|
||||
values = babbelbingo.get_all_values()
|
||||
list_values = [item for sublist in values for item in sublist]
|
||||
questions = random.sample(list_values, k=24)
|
||||
return questions
|
||||
|
||||
def make_bingocard(name, questions):
|
||||
image = Image.open('jeevesbot/files/bingokaart.png')
|
||||
font_name = ImageFont.truetype('jeevesbot/files/Overpass-Regular.ttf', 13)
|
||||
draw = ImageDraw.Draw(image)
|
||||
wrapper = textwrap.TextWrapper(width=20, break_on_hyphens=True)
|
||||
box01 = '\n'.join(wrapper.wrap(questions[0]))
|
||||
box02 = '\n'.join(wrapper.wrap(questions[1]))
|
||||
box03 = '\n'.join(wrapper.wrap(questions[2]))
|
||||
box04 = '\n'.join(wrapper.wrap(questions[3]))
|
||||
box05 = '\n'.join(wrapper.wrap(questions[4]))
|
||||
box06 = '\n'.join(wrapper.wrap(questions[5]))
|
||||
box07 = '\n'.join(wrapper.wrap(questions[6]))
|
||||
box08 = '\n'.join(wrapper.wrap(questions[7]))
|
||||
box09 = '\n'.join(wrapper.wrap(questions[8]))
|
||||
box10 = '\n'.join(wrapper.wrap(questions[9]))
|
||||
box11 = '\n'.join(wrapper.wrap(questions[10]))
|
||||
box12 = '\n'.join(wrapper.wrap(questions[11]))
|
||||
box13 = '\n'.join(wrapper.wrap(questions[12]))
|
||||
box14 = '\n'.join(wrapper.wrap(questions[13]))
|
||||
box15 = '\n'.join(wrapper.wrap(questions[14]))
|
||||
box16 = '\n'.join(wrapper.wrap(questions[15]))
|
||||
box17 = '\n'.join(wrapper.wrap(questions[16]))
|
||||
box18 = '\n'.join(wrapper.wrap(questions[17]))
|
||||
box19 = '\n'.join(wrapper.wrap(questions[18]))
|
||||
box20 = '\n'.join(wrapper.wrap(questions[19]))
|
||||
box21 = '\n'.join(wrapper.wrap(questions[20]))
|
||||
box22 = '\n'.join(wrapper.wrap(questions[21]))
|
||||
box23 = '\n'.join(wrapper.wrap(questions[22]))
|
||||
box24 = '\n'.join(wrapper.wrap(questions[23]))
|
||||
draw.multiline_text((95, 280.5), box01, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((229, 280.5), box02, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((363, 280.5), box03, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((497, 280.5), box04, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((631, 280.5), box05, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((95, 399.5), box06, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((229, 399.5), box07, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((363, 399.5), box08, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((497, 399.5), box09, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((631, 399.5), box10, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((95, 518.5), box11, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((229, 518.5), box12, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((497, 518.5), box13, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((631, 518.5), box14, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((95, 637.5), box15, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((229, 637.5), box16, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((363, 637.5), box17, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((497, 637.5), box18, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((631, 637.5), box19, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((95, 756.5), box20, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((229, 756.5), box21, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((363, 756.5), box22, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((497, 756.5), box23, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
draw.multiline_text((631, 756.5), box24, (0, 0, 0), font=font_name, align='center', anchor='mm', spacing=8)
|
||||
table = str.maketrans({'(':None, ')':None, '/':None, ' ':'_'})
|
||||
image.save('jeevesbot/files/generated_bingocards/' + str(name).translate(table) + '.png')
|
||||
|
||||
def bingo(name):
|
||||
questions = babbelbingo_file()
|
||||
make_bingocard(name, questions)
|
||||
table = str.maketrans({'(':None, ')':None, '/':None, ' ':'_'})
|
||||
bingoimage = ('jeevesbot/files/generated_bingocards/' + str(name).translate(table) + '.png')
|
||||
return bingoimage
|
||||
34
jeevesbot/database.py
Normal file
34
jeevesbot/database.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sqlite3
|
||||
|
||||
def init_db():
|
||||
conn = sqlite3.connect(r"jeevesbot/databases/reminders.db")
|
||||
c = conn.cursor()
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS reminders (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER,
|
||||
message TEXT,
|
||||
reminder_time TIMESTAMP
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def add_reminder(user_id, message, reminder_time):
|
||||
conn = sqlite3.connect(r"jeevesbot/databases/reminders.db")
|
||||
c = conn.cursor()
|
||||
c.execute('INSERT INTO reminders (user_id, message, reminder_time) VALUES (?, ?, ?)', (user_id, message, reminder_time))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def get_due_reminders(current_time):
|
||||
conn = sqlite3.connect(r"jeevesbot/databases/reminders.db")
|
||||
c = conn.cursor()
|
||||
c.execute('SELECT id, user_id, message FROM reminders WHERE reminder_time <= ?', (current_time,))
|
||||
reminders = c.fetchall()
|
||||
c.execute('DELETE FROM reminders WHERE reminder_time <= ?', (current_time,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return reminders
|
||||
@@ -2,7 +2,5 @@
|
||||
|
||||
TOKEN = 'discord-bot-token-here'
|
||||
ADMIN_ROLE = 'role-to-exclude-from-gifbot'
|
||||
GIFCHANNELS = 'add-channel-ids-for-bot-to-work-in'
|
||||
BOTCHANNEL = ['add id of bot channel']
|
||||
FLAIRROLES = ['add list of role ids to correspond to the emoji']
|
||||
FLAIRROLE = ['add id of flair separator role']
|
||||
GUILD_ID = 'id-of-guild'
|
||||
PREVIEWCHANNELS = ['add-channel-ids-for-bot-to-work-in']
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB |
@@ -29,4 +29,3 @@ def checkchannel(channelid):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
aiohttp
|
||||
async-timeout
|
||||
dice
|
||||
discord.py
|
||||
discord.py[voice]
|
||||
docopt
|
||||
multidict
|
||||
pyparsing
|
||||
typing-extensions
|
||||
pylint
|
||||
gspread
|
||||
oauth2client
|
||||
Pillow
|
||||
pytube
|
||||
sqlite3
|
||||
|
||||
# needs this version, otherwise TypeErrors will break stuff
|
||||
yarl==1.4.2
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import time
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import sys
|
||||
import ast
|
||||
from ..jeevesbot import env
|
||||
|
||||
## Standalone script for emoji-vvdd
|
||||
|
||||
# setup discord.py bot
|
||||
intents = discord.Intents().all()
|
||||
client = commands.Bot(command_prefix='!', intents=intents)
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
params = sys.argv[1:]
|
||||
else:
|
||||
print("Missing parameter input.")
|
||||
|
||||
async def score(question, score):
|
||||
e = discord.Embed(title='Emoji-VVDD', color=discord.Color.green())
|
||||
e.set_author(name='Jeeves', icon_url='https://cdn.hippogrief.nl/jeevesbot/jeeves.jpg')
|
||||
e.add_field(name=question, value='\u200b', inline=False)
|
||||
e.add_field(name='Hoogste score:', value=score, inline=False)
|
||||
e.set_thumbnail(url='https://cdn.hippogrief.nl/jeevesbot/logo.jpg')
|
||||
return score
|
||||
|
||||
async def run_script(params):
|
||||
params = params
|
||||
channel = client.get_channel(729667183126511617) # vvdd 729667183126511617 # tech 790908319005933598 # test 749399756752814105
|
||||
emoji_numbers = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣']
|
||||
question = params[0] # string
|
||||
answers = ast.literal_eval(params[1]) # list
|
||||
number_of_responses = len(answers) # int
|
||||
e = discord.Embed(title='Emoji-VVDD', description='Klik de emoji beneden dit bericht om te stemmen op het antwoord wat bij de emoji hoort.', color=discord.Color.blue())
|
||||
e.set_author(name='Jeeves', icon_url='https://cdn.hippogrief.nl/jeevesbot/jeeves.jpg')
|
||||
e.add_field(name=question, value='\u200b', inline=False)
|
||||
for emoji, answer in zip(emoji_numbers, answers):
|
||||
e.add_field(name=emoji, value=answer, inline=False)
|
||||
e.set_thumbnail(url='https://cdn.hippogrief.nl/jeevesbot/logo.jpg')
|
||||
message = await channel.send(embed=e)
|
||||
for i in range(number_of_responses):
|
||||
await message.add_reaction(emoji_numbers[i])
|
||||
for i in range(24, -1, -1):
|
||||
time.sleep(60)
|
||||
message = await channel.fetch_message(message.id)
|
||||
|
||||
values = {}
|
||||
for i in message.reactions:
|
||||
itervalues = {str(i): int(i.count)}
|
||||
values.update(itervalues)
|
||||
max_key = max(values, key=values.get)
|
||||
all_values = values.values()
|
||||
max_value = max(all_values)
|
||||
highest_keys = [key for key in values if values[key] == max_value]
|
||||
|
||||
if len(highest_keys) == 1:
|
||||
max_key = highest_keys[0]
|
||||
hoogste_score = 'Hoogste score:'
|
||||
elif len(highest_keys) != 1:
|
||||
max_key = highest_keys
|
||||
print(max_key)
|
||||
hoogste_score = 'Gelijkspel tussen:'
|
||||
|
||||
score = (str(max_key) + ' met ' + str(max_value) + ' stemmen.')
|
||||
f = discord.Embed(title='Emoji-VVDD', color=discord.Color.green())
|
||||
f.set_author(name='Jeeves', icon_url='https://cdn.hippogrief.nl/jeevesbot/jeeves.jpg')
|
||||
f.add_field(name=question, value='\u200b', inline=False)
|
||||
f.add_field(name=hoogste_score, value=score, inline=False)
|
||||
f.set_footer(text="Je mag blijven doorstemmen, deze tussenstand zal niet worden bijgewerkt meer verder.")
|
||||
f.set_thumbnail(url='https://cdn.hippogrief.nl/jeevesbot/logo.jpg')
|
||||
message = await channel.send(embed=f)
|
||||
|
||||
await client.close()
|
||||
|
||||
@client.event
|
||||
async def on_ready():
|
||||
print('### Active with id %s as %s ###' % (client.user.id,client.user.name) )
|
||||
activity = discord.Activity(name='!help', type=discord.ActivityType.listening)
|
||||
await client.change_presence(activity=activity)
|
||||
await run_script(params)
|
||||
|
||||
if __name__ == '__main__':
|
||||
client.run(env.TOKEN)
|
||||
12
scripts/jeeves.service
Normal file
12
scripts/jeeves.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Jeeves Discord Bot
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=<username>
|
||||
WorkingDirectory=/path/to/bot/directory
|
||||
ExecStart=/usr/bin/python3 /path/to/bot/directory/bot.py
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user