Compare commits

20 Commits
v2.0.0 ... main

Author SHA1 Message Date
f51afc134a remove header tags 2025-03-30 20:52:20 +02:00
aa15d5c026 Merge branch 'main' of github.com:pvanarkel/discord-jeeves into main 2025-03-30 20:51:19 +02:00
5721939412 updated functions.py 2025-03-30 20:49:57 +02:00
Peter van Arkel
d194332843 Merge pull request #43 from pvanarkel/41-refactor_for_async_slashcommands
added first slashcommand
2024-07-17 15:54:05 +02:00
fa2e4a78cd added first slashcommand and refactored the preview cog as result of breaking changes in slashcommand 2024-07-17 15:52:39 +02:00
Peter van Arkel
27c9dd353f Merge pull request #42 from pvanarkel/prepare_for_refactor
Prepare for refactor
2024-07-16 23:45:09 +02:00
ab1fca82dc update README.md because secrets.json is not necessary anymore 2024-07-16 23:42:30 +02:00
ceed2ad105 Remove all code for babbelbingo, also remove requirements for this code. 2024-07-16 23:39:39 +02:00
9fd6701f53 Removed unused parameters of the FLAIR cog and related code 2024-07-16 22:58:18 +02:00
Peter van Arkel
c8c33e6f25 Merge pull request #39 from pvanarkel/prepare_for_refactor
Prepare jeeves repository for refactor. Removed obsolete commands.
2024-07-16 16:52:41 +02:00
Peter van Arkel
1fc1ac5feb Update LICENSE.md 2024-07-16 16:52:04 +02:00
c565e41a4b Prepare jeeves repository for refactor. Removed obsolete commands. 2024-07-16 16:49:32 +02:00
9b3642c92b Merge branch 'main' of github.com:pvanarkel/discord-jeeves into main 2024-07-16 16:19:00 +02:00
eb7d4de9e1 running config to main 2024-07-16 16:15:27 +02:00
Peter van Arkel
bf37ba09b6 Merge pull request #38 from pvanarkel/version_2_1
- Removed flair commands from 2.1 as they are now obsoleted by Discord Onboarding.
- Removed documentation and extra code used by flair code.
- Removed Makefile in favor of systemd daemons.
- Quick setup steps in README.md, needs to be built out eventually.
2023-06-27 21:24:44 +02:00
c72aa1df67 Cleanup edits for 2.1 2023-06-27 21:20:32 +02:00
Peter van Arkel
e7f39ac82b Update FUNDING.yml 2023-05-22 10:07:02 +02:00
Peter van Arkel
83a97aeef0 Merge pull request #36 from pvanarkel/34-rework-gifcog
rename gif cog to preview.py and add youtube preview functionality
2023-03-24 10:07:22 +01:00
337f4abfcd rename cog to preview.py and add youtube preview functionality 2023-03-24 10:04:50 +01:00
a1b47248e3 No quotes in list 2023-03-23 10:16:12 +01:00
26 changed files with 298 additions and 467 deletions

2
.github/FUNDING.yml vendored
View File

@@ -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

6
.gitignore vendored
View File

@@ -1,9 +1,9 @@
jeevesbot/env.py
jeevesbot/secret.json
jeevesbot/databases/*
jeevesbot/__pycache__/*
logs/
__init__.py
*.pyc
__pycache__/*
cogs/__pycache__/*
jeevesbot/__pycache__/*
.vscode/
.vscode/

View File

@@ -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,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@@ -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__/

View File

@@ -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`

View File

@@ -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):

View File

@@ -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))

View File

@@ -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!
@@ -68,4 +59,4 @@ class Games(commands.Cog):
async def setup(bot):
await bot.add_cog(Games(bot))
await bot.add_cog(Games(bot))

View File

@@ -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))

View File

@@ -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.')
@@ -34,4 +26,4 @@ class Misc(commands.Cog):
async def setup(bot):
await bot.add_cog(Misc(bot))
await bot.add_cog(Misc(bot))

107
cogs/preview.py Normal file
View 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
View 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')

View File

@@ -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))

View File

@@ -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.*

View File

@@ -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`
@@ -14,4 +11,4 @@
`!organisaties`
** Misc **
`!eirik`
`!eirik`

View File

@@ -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 **
@@ -23,4 +24,4 @@
`!roll 2d6rr(N)`
rollt alle resultaten beneden (N) opnieuw. De reroll zal minimaal (N) zijn, mogelijk hoger.
`!roll 10d10s`
Sorteert alle rolls van laag naar hoog, dit veranderd het resultaat verder niet.
Sorteert alle rolls van laag naar hoog, dit veranderd het resultaat verder niet.

114
jeeves.py
View File

@@ -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()
@bot.command(name='load', hidden=True)
@commands.has_permissions(administrator=True)
async def load(ctx, extension):
bot.load_extension(f'cogs.{extension}')
log.info(f'{ctx.message.author} loaded the {extension} module')
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='unload', hidden=True)
@commands.has_permissions(administrator=True)
async def unload(ctx, extension):
bot.unload_extension(f'cogs.{extension}')
log.info(f'{ctx.message.author} unloaded the {extension} module')
@commands.command(name='load', hidden=True)
@commands.has_permissions(administrator=True)
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='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}')
log.info(f'{ctx.message.author} reloaded the {extension} module')
@commands.command(name='unload', hidden=True)
@commands.has_permissions(administrator=True)
async def unload(self, ctx, extension):
self.unload_extension(f'cogs.{extension}')
log.info(f'{ctx.message.author} unloaded the {extension} module')
async def load_extensions():
for filename in os.listdir('./cogs'):
if filename.endswith('.py'):
await bot.load_extension(f'cogs.{filename[:-3]}')
@commands.command(name='reload', hidden=True)
@commands.has_permissions(administrator=True)
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')
@bot.event
async def on_ready():
log.info(f'Active with ID:{bot.user.id} as {bot.user.name}')
activity = discord.Activity(name='!help', type=discord.ActivityType.listening)
await bot.change_presence(activity=activity)
async def load_extensions(self):
for filename in os.listdir('./cogs'):
if filename.endswith('.py'):
await self.load_extension(f'cogs.{filename[:-3]}')
@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 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()
await bot.start(env.TOKEN)
bot = Jeeves()
await bot.load_extensions()
await bot.start(env.TOKEN)
asyncio.run(main())
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -29,4 +29,3 @@ def checkchannel(channelid):
return True
else:
return False

View File

@@ -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

View File

@@ -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
View 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