Merge branch 'master' into werewolf_develop
# Conflicts: # werewolf/builder.py # werewolf/game.py # werewolf/werewolf.py
This commit is contained in:
commit
3296c03de2
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,5 @@
|
||||
.idea/
|
||||
*.pyc
|
||||
venv/
|
||||
v-data/
|
||||
database.sqlite3
|
||||
|
26
README.md
26
README.md
@ -3,19 +3,35 @@
|
||||
Cog Function
|
||||
|
||||
| Name | Status | Description (Click to see full status)
|
||||
| --- | --- | --- |
|
||||
| --- | --- | --- |
|
||||
| announcedaily | **Alpha** | <details><summary>Send daily announcements to all servers at a specified times</summary>Commissioned release, so suggestions will not be accepted</details> |
|
||||
| audiotrivia | **Alpha** | <details><summary>Guess the audio using the core trivia cog</summary>Replaces the core Trivia cog. Needs help adding audio trivia lists, please submit a PR to contribute</details> |
|
||||
| ccrole | **Beta** | <details><summary>Create custom commands that also assign roles</summary>May have some bugs, please create an issue if you find any</details> |
|
||||
| chatter | **Alpha** | <details><summary>Chat-bot trained to talk like your guild</summary>Missing some key features, but currently functional</details> |
|
||||
| coglint | **Alpha** | <details><summary>Error check code in python syntax posted to discord</summary>Works, but probably needs more turning to work for cogs</details> |
|
||||
| exclusiverole | **Alpha** | <details><summary>Prevent certain roles from getting any other roles</summary>Fully functional, but pretty simple</details> |
|
||||
| fight | **Incomplete** | <details><summary>Organize bracket tournaments within discord</summary>Still in-progress, a massive project</details> |
|
||||
| flag | **Incomplete** | <details><summary>Create temporary marks on users that expire after specified time</summary>Not yet ported to v3</details> |
|
||||
| flag | **Alpha** | <details><summary>Create temporary marks on users that expire after specified time</summary>Ported, will not import old data. Please report bugs</details> |
|
||||
| forcemention | **Alpha** | <details><summary>Mentions unmentionable roles</summary>Very simple cog, mention doesn't persist</details> |
|
||||
| hangman | **Alpha** | <details><summary>Play a game of hangman</summary>Some visual glitches and needs more customization</details> |
|
||||
| howdoi | **Incomplete** | <details><summary>Create temporary marks on users that expire after specified time</summary>Not yet ported to v3</details> |
|
||||
| leaver | **Incomplete** | <details><summary>Send a message in a channel when a user leaves the server</summary>Not yet ported to v3</details> |
|
||||
| howdoi | **Incomplete** | <details><summary>Ask coding questions and get results from StackExchange</summary>Not yet functional</details> |
|
||||
| leaver | **Alpha** | <details><summary>Send a message in a channel when a user leaves the server</summary>Just released, please report bugs</details> |
|
||||
| lovecalculator | **Alpha** | <details><summary>Calculate the love between two users</summary>[Snap-Ons] Just updated to V3</details> |
|
||||
| lseen | **Alpha** | <details><summary>Track when a member was last online</summary>Alpha release, please report bugs</details> |
|
||||
| nudity | **Incomplete** | <details><summary>Checks for NSFW images posted in non-NSFW channels</summary>Library this is based on has a bug, waiting for author to merge my PR</details> |
|
||||
| planttycoon | **Alpha** | <details><summary>Grow your own plants!</summary>[Snap-Ons] Updated to V3, likely to contain bugs</details> |
|
||||
| qrinvite | **Alpha** | <details><summary>Create a QR code invite for the server</summary>Alpha release, please report any bugs</details> |
|
||||
| reactrestrict | **Alpha** | <details><summary>Removes reactions by role per channel</summary>A bit clunky, but functional</details> |
|
||||
| recyclingplant | **Alpha** | <details><summary>Work at a recycling plant</summary>[Snap-Ons] Just updated to V3</details> |
|
||||
| rpsls | **Alpha** | <details><summary>Play Rock-Paper-Scissors-Lizard-Spock</summary>[Snap-Ons] Just updated to V3</details> |
|
||||
| sayurl | **Alpha** | <details><summary>Convert any URL into text and post to discord</summary>No error checking and pretty spammy</details> |
|
||||
| scp | **Alpha** | <details><summary>Look-up SCP articles</summary>[Snap-Ons] Just updated to V3</details> |
|
||||
| secrethitler | **Incomplete** | <details><summary>Play the Secret Hitler game</summary>Concept, no work done yet</details> |
|
||||
| stealemoji | **Alpha** | <details><summary>Steals any custom emoji it sees in a reaction</summary>Some planned upgrades for server generation</details> |
|
||||
| werewolf | **Alpha** | <details><summary>Play the classic party game Werewolf within discord</summary>Another massive project currently being developed, will be fully customizable</details> |
|
||||
| timerole | **Alpha** | <details><summary>Add roles to members after specified time on the server</summary>Upgraded from V2, please report any bugs</details> |
|
||||
| tts | **Beta** | <details><summary>Send a Text-to-Speech message as an uploaded mp3</summary>Alpha release, please report any bugs</details> |
|
||||
| unicode | **Alpha** | <details><summary>Encode and Decode unicode characters</summary>[Snap-Ons] Just updated to V3</details> |
|
||||
| werewolf | **Pre-Alpha** | <details><summary>Play the classic party game Werewolf within discord</summary>Another massive project currently being developed, will be fully customizable</details> |
|
||||
|
||||
|
||||
Check out my V2 cogs at [Fox-Cogs v2](https://github.com/bobloy/Fox-Cogs)
|
||||
|
9
announcedaily/__init__.py
Normal file
9
announcedaily/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from redbot.core.bot import Red
|
||||
|
||||
from .announcedaily import AnnounceDaily
|
||||
|
||||
|
||||
def setup(bot: Red):
|
||||
daily = AnnounceDaily(bot)
|
||||
bot.add_cog(daily)
|
||||
bot.loop.create_task(daily.check_day())
|
253
announcedaily/announcedaily.py
Normal file
253
announcedaily/announcedaily.py
Normal file
@ -0,0 +1,253 @@
|
||||
import asyncio
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
import discord
|
||||
from redbot.core import Config, checks, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.utils.chat_formatting import pagify, box
|
||||
|
||||
DEFAULT_MESSAGES = [
|
||||
# "Example message. Uncomment and overwrite to use",
|
||||
# "Example message 2. Each message is in quotes and separated by a comma"
|
||||
]
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class AnnounceDaily(Cog):
|
||||
"""
|
||||
Send daily announcements
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.path = str(cog_data_path(self)).replace('\\', '/')
|
||||
|
||||
self.image_path = self.path + "/"
|
||||
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_global = {
|
||||
'messages': [],
|
||||
'images': [],
|
||||
'time': {'hour': 0, 'minute': 0, 'second': 0}
|
||||
}
|
||||
default_guild = {
|
||||
"channelid": None
|
||||
}
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
async def _get_msgs(self):
|
||||
return DEFAULT_MESSAGES + await self.config.messages()
|
||||
|
||||
@commands.group(name="announcedaily", aliases=['annd'])
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@commands.guild_only()
|
||||
async def _ad(self, ctx: commands.Context):
|
||||
"""
|
||||
Base command for managing AnnounceDaily settings
|
||||
|
||||
Do `[p]help annd <subcommand>` for more details
|
||||
"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
pass
|
||||
|
||||
@commands.command()
|
||||
@checks.guildowner()
|
||||
@commands.guild_only()
|
||||
async def runannounce(self, ctx: commands.Context):
|
||||
"""Manually run the daily announcement"""
|
||||
|
||||
await self.send_announcements()
|
||||
await ctx.send("Success")
|
||||
|
||||
@_ad.command()
|
||||
async def setchannel(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||
"""
|
||||
Set the announcement channel for this server
|
||||
|
||||
Don't pass a channel to clear this server of receiving announcements
|
||||
"""
|
||||
if channel is not None:
|
||||
await self.config.guild(ctx.guild).channelid.set(channel.id)
|
||||
await ctx.send("Announcement channel has been set to {}".format(channel.mention))
|
||||
else:
|
||||
await self.config.guild(ctx.guild).channelid.set(None)
|
||||
await ctx.send("Announcement channel has been cleared")
|
||||
|
||||
@_ad.command()
|
||||
async def addmsg(self, ctx: commands.Context, *, msg):
|
||||
"""
|
||||
Add a message to the pool of announcement messages
|
||||
"""
|
||||
async with self.config.messages() as msgs:
|
||||
msgs.append(msg)
|
||||
|
||||
await ctx.send("Message successfully added!")
|
||||
|
||||
@_ad.command()
|
||||
async def addimg(self, ctx: commands.Context, filename=None):
|
||||
"""
|
||||
Add an image to the pool of announcement images
|
||||
|
||||
You must attach an image while executing this command
|
||||
"""
|
||||
if ctx.message.attachments:
|
||||
att_ = ctx.message.attachments[0]
|
||||
try:
|
||||
h = att_.height
|
||||
except AttributeError:
|
||||
await ctx.send("You must attach an image, no other file will be accepted")
|
||||
return
|
||||
|
||||
if filename is None:
|
||||
filename = att_.filename
|
||||
|
||||
try:
|
||||
# with open(self.image_path + filename, 'w') as f:
|
||||
# await att_.save(f)
|
||||
await att_.save(self.image_path + filename)
|
||||
except discord.NotFound:
|
||||
await ctx.send("Did you delete the message? Cause I couldn't download the attachment")
|
||||
except discord.HTTPException:
|
||||
await ctx.send("Failed to download the attachment, please try again")
|
||||
else:
|
||||
async with self.config.images() as images:
|
||||
if filename in images:
|
||||
await ctx.send("Image {} has been overwritten!".format(filename))
|
||||
else:
|
||||
images.append(filename)
|
||||
await ctx.send("Image {} has been added!".format(filename))
|
||||
else:
|
||||
await ctx.send("You must attach an image when sending this command")
|
||||
|
||||
@_ad.command()
|
||||
async def listmsg(self, ctx: commands.Context):
|
||||
"""
|
||||
List all registered announcement messages
|
||||
"""
|
||||
messages = await self.config.messages()
|
||||
for page in pagify("\n".join("{} - {}".format(key, image) for key, image in enumerate(messages))):
|
||||
await ctx.send(box(page))
|
||||
await ctx.send("Done!")
|
||||
|
||||
@_ad.command()
|
||||
async def listimg(self, ctx: commands.Context):
|
||||
"""
|
||||
List all registered announcement immages
|
||||
"""
|
||||
images = await self.config.images()
|
||||
for page in pagify("\n".join(images)):
|
||||
await ctx.send(box(page))
|
||||
await ctx.send("Done!")
|
||||
|
||||
@_ad.command()
|
||||
async def delmsg(self, ctx: commands.Context, index: int):
|
||||
"""
|
||||
Remove a message from the announcement pool
|
||||
|
||||
Must provide the index of the message, which can be found by using `[p]annd listmsg`
|
||||
"""
|
||||
async with self.config.messages() as messages:
|
||||
try:
|
||||
out = messages.pop(index)
|
||||
except IndexError:
|
||||
await ctx.send("Invalid index, check valid indexes with `listmsg` command")
|
||||
return
|
||||
|
||||
await ctx.send("The following message was removed:\n```{}```".format(out))
|
||||
|
||||
@_ad.command()
|
||||
async def delimg(self, ctx: commands.Context, filename: str):
|
||||
"""
|
||||
Remove an image from the announcement pool
|
||||
|
||||
Does not delete the file from the disk, so you may have to clean it up occasionally
|
||||
"""
|
||||
async with self.config.images() as images:
|
||||
if filename not in images:
|
||||
await ctx.send("This file doesn't exist")
|
||||
else:
|
||||
images.remove(filename)
|
||||
await ctx.send("Successfully removed {}".format(filename))
|
||||
|
||||
@_ad.command()
|
||||
async def settime(self, ctx: commands.Context, minutes_from_now: int):
|
||||
"""
|
||||
Set the daily announcement time
|
||||
|
||||
It will first announce at the time you provided, then it will repeat every 24 hours
|
||||
"""
|
||||
ann_time = datetime.now() + timedelta(minutes=minutes_from_now)
|
||||
|
||||
h = ann_time.hour
|
||||
m = ann_time.minute
|
||||
s = ann_time.second
|
||||
await self.config.time.set({'hour': h, 'minute': m, 'second': s})
|
||||
|
||||
await ctx.send("Announcements time has been set to {}::{}::{} every day\n"
|
||||
"**Changes will apply after next scheduled announcement or reload**".format(h, m, s))
|
||||
|
||||
async def send_announcements(self):
|
||||
messages = await self._get_msgs()
|
||||
images = await self.config.images()
|
||||
|
||||
total = len(messages) + len(images)
|
||||
if total < 1:
|
||||
return
|
||||
|
||||
x = random.randint(0, total - 1)
|
||||
|
||||
if x >= len(messages):
|
||||
x -= len(messages)
|
||||
choice = images[x]
|
||||
choice = open(self.image_path + choice, 'rb')
|
||||
is_image = True
|
||||
else:
|
||||
choice = messages[x]
|
||||
is_image = False
|
||||
|
||||
for guild in self.bot.guilds:
|
||||
channel = await self.config.guild(guild).channelid()
|
||||
if channel is None:
|
||||
continue
|
||||
channel = guild.get_channel(channel)
|
||||
if channel is None:
|
||||
continue
|
||||
|
||||
if is_image:
|
||||
await channel.send(file=discord.File(choice))
|
||||
else:
|
||||
await channel.send(choice)
|
||||
|
||||
async def check_day(self):
|
||||
while self is self.bot.get_cog("AnnounceDaily"):
|
||||
tomorrow = datetime.now() + timedelta(days=1)
|
||||
time = await self.config.time()
|
||||
h, m, s = time['hour'], time['minute'], time['second']
|
||||
midnight = datetime(year=tomorrow.year, month=tomorrow.month,
|
||||
day=tomorrow.day, hour=h, minute=m, second=s)
|
||||
|
||||
print("Sleeping for {} seconds".format((midnight - datetime.now()).seconds))
|
||||
await asyncio.sleep((midnight - datetime.now()).seconds)
|
||||
|
||||
if self is not self.bot.get_cog("AnnounceDaily"):
|
||||
print("Announce canceled, cog has been lost")
|
||||
return
|
||||
|
||||
await self.send_announcements()
|
||||
|
||||
await asyncio.sleep(3)
|
||||
|
||||
# [p]setchannel #channelname - Set the announcement channel per server
|
||||
# [p]addmsg <message goes here> - Adds a msg to the pool
|
||||
# [p]addimg http://imgurl.com/image.jpg - Adds an image to the pool
|
||||
# [p]listmsg - Lists all messages in the pool
|
||||
# [p]listimg - Unsure about this one, but would probably just post all the images
|
||||
# [p]delmsg - Remove msg from pool
|
||||
# [p]delimg - Remove image from pool
|
||||
# [p]settime <x> - S
|
18
announcedaily/info..json
Normal file
18
announcedaily/info..json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Send daily announcements to all servers at a specified times",
|
||||
"hidden": true,
|
||||
"install_msg": "Thank you for installing AnnounceDaily! Get started with `[p]load announcedaily` and `[p]help AnnounceDaily`",
|
||||
"requirements": [],
|
||||
"short": "Send daily announcements",
|
||||
"tags": [
|
||||
"bobloy"
|
||||
]
|
||||
}
|
13
audiotrivia/__init__.py
Normal file
13
audiotrivia/__init__.py
Normal file
@ -0,0 +1,13 @@
|
||||
from redbot.core.bot import Red
|
||||
|
||||
from .audiotrivia import AudioTrivia
|
||||
|
||||
|
||||
async def setup(bot: Red):
|
||||
if bot.get_cog("Trivia"):
|
||||
print("Trivia is already loaded, attempting to unload it first")
|
||||
bot.remove_cog("Trivia")
|
||||
await bot.remove_loaded_package("trivia")
|
||||
bot.unload_extension("trivia")
|
||||
|
||||
bot.add_cog(AudioTrivia(bot))
|
74
audiotrivia/audiosession.py
Normal file
74
audiotrivia/audiosession.py
Normal file
@ -0,0 +1,74 @@
|
||||
"""Module to manage audio trivia sessions."""
|
||||
import asyncio
|
||||
|
||||
import lavalink
|
||||
from redbot.cogs.trivia import TriviaSession
|
||||
|
||||
|
||||
class AudioSession(TriviaSession):
|
||||
"""Class to run a session of audio trivia"""
|
||||
|
||||
def __init__(self, ctx, question_list: dict, settings: dict, player: lavalink.Player):
|
||||
super().__init__(ctx, question_list, settings)
|
||||
|
||||
self.player = player
|
||||
|
||||
@classmethod
|
||||
def start(cls, ctx, question_list, settings, player: lavalink.Player = None):
|
||||
session = cls(ctx, question_list, settings, player)
|
||||
loop = ctx.bot.loop
|
||||
session._task = loop.create_task(session.run())
|
||||
return session
|
||||
|
||||
async def run(self):
|
||||
"""Run the audio trivia session.
|
||||
|
||||
In order for the trivia session to be stopped correctly, this should
|
||||
only be called internally by `TriviaSession.start`.
|
||||
"""
|
||||
await self._send_startup_msg()
|
||||
max_score = self.settings["max_score"]
|
||||
delay = self.settings["delay"]
|
||||
timeout = self.settings["timeout"]
|
||||
for question, answers in self._iter_questions():
|
||||
async with self.ctx.typing():
|
||||
await asyncio.sleep(3)
|
||||
self.count += 1
|
||||
await self.player.stop()
|
||||
|
||||
msg = "**Question number {}!**\n\nName this audio!".format(self.count)
|
||||
await self.ctx.send(msg)
|
||||
# print("Audio question: {}".format(question))
|
||||
|
||||
# await self.ctx.invoke(self.audio.play(ctx=self.ctx, query=question))
|
||||
# ctx_copy = copy(self.ctx)
|
||||
|
||||
# await self.ctx.invoke(self.player.play, query=question)
|
||||
query = question.strip("<>")
|
||||
tracks = await self.player.get_tracks(query)
|
||||
seconds = tracks[0].length / 1000
|
||||
|
||||
if self.settings["repeat"] and seconds < delay:
|
||||
tot_length = seconds + 0
|
||||
while tot_length < delay:
|
||||
self.player.add(self.ctx.author, tracks[0])
|
||||
tot_length += seconds
|
||||
else:
|
||||
self.player.add(self.ctx.author, tracks[0])
|
||||
|
||||
if not self.player.current:
|
||||
await self.player.play()
|
||||
|
||||
continue_ = await self.wait_for_answer(answers, delay, timeout)
|
||||
if continue_ is False:
|
||||
break
|
||||
if any(score >= max_score for score in self.scores.values()):
|
||||
await self.end_game()
|
||||
break
|
||||
else:
|
||||
await self.ctx.send("There are no more questions!")
|
||||
await self.end_game()
|
||||
|
||||
async def end_game(self):
|
||||
await super().end_game()
|
||||
await self.player.disconnect()
|
212
audiotrivia/audiotrivia.py
Normal file
212
audiotrivia/audiotrivia.py
Normal file
@ -0,0 +1,212 @@
|
||||
import datetime
|
||||
import pathlib
|
||||
from typing import List
|
||||
|
||||
import lavalink
|
||||
import yaml
|
||||
from redbot.cogs.audio import Audio
|
||||
from redbot.cogs.trivia import LOG
|
||||
from redbot.cogs.trivia.trivia import InvalidListError, Trivia
|
||||
from redbot.core import commands, Config, checks
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
|
||||
from .audiosession import AudioSession
|
||||
|
||||
|
||||
class AudioTrivia(Trivia):
|
||||
"""
|
||||
Custom commands
|
||||
Creates commands used to display text and adjust roles
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.audio = None
|
||||
self.audioconf = Config.get_conf(self, identifier=651171001051118411410511810597, force_registration=True)
|
||||
|
||||
self.audioconf.register_guild(
|
||||
delay=30.0,
|
||||
repeat=True,
|
||||
)
|
||||
|
||||
@commands.group()
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def atriviaset(self, ctx: commands.Context):
|
||||
"""Manage Audio Trivia settings."""
|
||||
audioset = self.audioconf.guild(ctx.guild)
|
||||
settings_dict = await audioset.all()
|
||||
msg = box(
|
||||
"**Audio settings**\n"
|
||||
"Answer time limit: {delay} seconds\n"
|
||||
"Repeat Short Audio: {repeat}"
|
||||
"".format(**settings_dict),
|
||||
lang="py",
|
||||
)
|
||||
await ctx.send(msg)
|
||||
|
||||
@atriviaset.command(name="delay")
|
||||
async def atriviaset_delay(self, ctx: commands.Context, seconds: float):
|
||||
"""Set the maximum seconds permitted to answer a question."""
|
||||
if seconds < 4.0:
|
||||
await ctx.send("Must be at least 4 seconds.")
|
||||
return
|
||||
settings = self.audioconf.guild(ctx.guild)
|
||||
await settings.delay.set(seconds)
|
||||
await ctx.send("Done. Maximum seconds to answer set to {}.".format(seconds))
|
||||
|
||||
@atriviaset.command(name="repeat")
|
||||
async def atriviaset_repeat(self, ctx: commands.Context, true_or_false: bool):
|
||||
"""Set whether or not short audio will be repeated"""
|
||||
settings = self.audioconf.guild(ctx.guild)
|
||||
await settings.repeat.set(true_or_false)
|
||||
await ctx.send("Done. Repeating short audio is now set to {}.".format(true_or_false))
|
||||
|
||||
@commands.group(invoke_without_command=True)
|
||||
@commands.guild_only()
|
||||
async def audiotrivia(self, ctx: commands.Context, *categories: str):
|
||||
"""Start trivia session on the specified category.
|
||||
|
||||
You may list multiple categories, in which case the trivia will involve
|
||||
questions from all of them.
|
||||
"""
|
||||
if not categories and ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
return
|
||||
|
||||
if self.audio is None:
|
||||
self.audio: Audio = self.bot.get_cog("Audio")
|
||||
|
||||
if self.audio is None:
|
||||
await ctx.send("Audio is not loaded. Load it and try again")
|
||||
return
|
||||
|
||||
categories = [c.lower() for c in categories]
|
||||
session = self._get_trivia_session(ctx.channel)
|
||||
if session is not None:
|
||||
await ctx.send("There is already an ongoing trivia session in this channel.")
|
||||
return
|
||||
|
||||
status = await self.audio.config.status()
|
||||
|
||||
if status:
|
||||
await ctx.send("I recommend disabling audio status with `{}audioset status`".format(ctx.prefix))
|
||||
|
||||
if not self.audio._player_check(ctx):
|
||||
try:
|
||||
if not ctx.author.voice.channel.permissions_for(ctx.me).connect or self.audio._userlimit(
|
||||
ctx.author.voice.channel
|
||||
):
|
||||
return await ctx.send("I don't have permission to connect to your channel."
|
||||
)
|
||||
await lavalink.connect(ctx.author.voice.channel)
|
||||
lavaplayer = lavalink.get_player(ctx.guild.id)
|
||||
lavaplayer.store("connect", datetime.datetime.utcnow())
|
||||
except AttributeError:
|
||||
return await ctx.send("Connect to a voice channel first.")
|
||||
|
||||
lavaplayer = lavalink.get_player(ctx.guild.id)
|
||||
lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno
|
||||
lavaplayer.store("guild", ctx.guild.id)
|
||||
|
||||
await self.audio._data_check(ctx)
|
||||
|
||||
if (
|
||||
not ctx.author.voice or ctx.author.voice.channel != lavaplayer.channel
|
||||
):
|
||||
return await ctx.send("You must be in the voice channel to use the audiotrivia command.")
|
||||
|
||||
trivia_dict = {}
|
||||
authors = []
|
||||
for category in reversed(categories):
|
||||
# We reverse the categories so that the first list's config takes
|
||||
# priority over the others.
|
||||
try:
|
||||
dict_ = self.get_audio_list(category)
|
||||
except FileNotFoundError:
|
||||
await ctx.send(
|
||||
"Invalid category `{0}`. See `{1}audiotrivia list`"
|
||||
" for a list of trivia categories."
|
||||
"".format(category, ctx.prefix)
|
||||
)
|
||||
except InvalidListError:
|
||||
await ctx.send(
|
||||
"There was an error parsing the trivia list for"
|
||||
" the `{}` category. It may be formatted"
|
||||
" incorrectly.".format(category)
|
||||
)
|
||||
else:
|
||||
trivia_dict.update(dict_)
|
||||
authors.append(trivia_dict.pop("AUTHOR", None))
|
||||
continue
|
||||
return
|
||||
if not trivia_dict:
|
||||
await ctx.send(
|
||||
"The trivia list was parsed successfully, however it appears to be empty!"
|
||||
)
|
||||
return
|
||||
settings = await self.conf.guild(ctx.guild).all()
|
||||
audiosettings = await self.audioconf.guild(ctx.guild).all()
|
||||
config = trivia_dict.pop("CONFIG", None)
|
||||
if config and settings["allow_override"]:
|
||||
settings.update(config)
|
||||
settings["lists"] = dict(zip(categories, reversed(authors)))
|
||||
|
||||
# Delay in audiosettings overwrites delay in settings
|
||||
combined_settings = {**settings, **audiosettings}
|
||||
session = AudioSession.start(ctx=ctx, question_list=trivia_dict, settings=combined_settings, player=lavaplayer)
|
||||
self.trivia_sessions.append(session)
|
||||
LOG.debug("New audio trivia session; #%s in %d", ctx.channel, ctx.guild.id)
|
||||
|
||||
@audiotrivia.command(name="list")
|
||||
@commands.guild_only()
|
||||
async def audiotrivia_list(self, ctx: commands.Context):
|
||||
"""List available trivia categories."""
|
||||
lists = set(p.stem for p in self._audio_lists())
|
||||
|
||||
msg = box("**Available trivia lists**\n\n{}".format(", ".join(sorted(lists))))
|
||||
if len(msg) > 1000:
|
||||
await ctx.author.send(msg)
|
||||
return
|
||||
await ctx.send(msg)
|
||||
|
||||
def get_audio_list(self, category: str) -> dict:
|
||||
"""Get the audiotrivia list corresponding to the given category.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
category : str
|
||||
The desired category. Case sensitive.
|
||||
|
||||
Returns
|
||||
-------
|
||||
`dict`
|
||||
A dict mapping questions (`str`) to answers (`list` of `str`).
|
||||
|
||||
"""
|
||||
try:
|
||||
path = next(p for p in self._audio_lists() if p.stem == category)
|
||||
except StopIteration:
|
||||
raise FileNotFoundError("Could not find the `{}` category.".format(category))
|
||||
|
||||
with path.open(encoding="utf-8") as file:
|
||||
try:
|
||||
dict_ = yaml.load(file)
|
||||
except yaml.error.YAMLError as exc:
|
||||
raise InvalidListError("YAML parsing failed.") from exc
|
||||
else:
|
||||
return dict_
|
||||
|
||||
def _audio_lists(self) -> List[pathlib.Path]:
|
||||
personal_lists = [p.resolve() for p in cog_data_path(self).glob("*.yaml")]
|
||||
|
||||
return personal_lists + get_core_lists()
|
||||
|
||||
|
||||
def get_core_lists() -> List[pathlib.Path]:
|
||||
"""Return a list of paths for all trivia lists packaged with the bot."""
|
||||
core_lists_path = pathlib.Path(__file__).parent.resolve() / "data/lists"
|
||||
return list(core_lists_path.glob("*.yaml"))
|
106
audiotrivia/data/lists/csgo.yaml
Normal file
106
audiotrivia/data/lists/csgo.yaml
Normal file
@ -0,0 +1,106 @@
|
||||
AUTHOR: bobloy
|
||||
https://www.youtube.com/watch?v=nfjiy-NX5b0:
|
||||
- flashbang
|
||||
https://www.youtube.com/watch?v=mJCE7s4W4IE:
|
||||
- starting round
|
||||
- round start
|
||||
- start round
|
||||
https://www.youtube.com/watch?v=XfLGi4cPu0Y:
|
||||
- select team
|
||||
- team select
|
||||
https://www.youtube.com/watch?v=b6ScVgFs-DQ:
|
||||
- desert eagle
|
||||
- deagle
|
||||
https://www.youtube.com/watch?v=JnHm-rn199Y:
|
||||
- planted bomb
|
||||
- bomb planted
|
||||
- bomb plant
|
||||
- plant bomb
|
||||
https://www.youtube.com/watch?v=3wztV24tbVU:
|
||||
- defusing bomb
|
||||
- defuse bomb
|
||||
- bomb defuse
|
||||
- bomb defusing
|
||||
https://www.youtube.com/watch?v=mpY9poBVje4:
|
||||
- lobby
|
||||
https://www.youtube.com/watch?v=zMT4ovCN7gk:
|
||||
- usp-s
|
||||
- usp s
|
||||
- usps
|
||||
https://www.youtube.com/watch?v=oI5Ww7y2aUQ:
|
||||
- gut knife
|
||||
https://www.youtube.com/watch?v=Dqmyxnx-OaQ:
|
||||
- ak47
|
||||
- ak 47
|
||||
https://www.youtube.com/watch?v=Ny4hGdziZP4:
|
||||
- hitmarker
|
||||
- hit
|
||||
- hitmaker
|
||||
- marker
|
||||
https://www.youtube.com/watch?v=vYUynDKM1Yw:
|
||||
- awp
|
||||
https://www.youtube.com/watch?v=52etXKmbQRM:
|
||||
- butterfly knife
|
||||
https://www.youtube.com/watch?v=99o4eyq0SzY:
|
||||
- won round
|
||||
- round won
|
||||
- win round
|
||||
- round win
|
||||
https://www.youtube.com/watch?v=V5tv1ZzqI_U:
|
||||
- lost round
|
||||
- round lost
|
||||
- lose round
|
||||
- round loss
|
||||
https://www.youtube.com/watch?v=1hI25OPdim0:
|
||||
- flashbang toss
|
||||
- toss flashbang
|
||||
- throwing flashbang
|
||||
- throw flashbang
|
||||
- flashbang throwing
|
||||
- flashbang throw
|
||||
- tossing flashbang
|
||||
- flashbang tossing
|
||||
https://www.youtube.com/watch?v=oML0z2Aj_D4:
|
||||
- firegrenade toss
|
||||
- toss firegrenade
|
||||
- throwing firegrenade
|
||||
- throw firegrenade
|
||||
- firegrenade throwing
|
||||
- firegrenade throw
|
||||
- tossing firegrenade
|
||||
- firegrenade tossing
|
||||
- fire grenade toss
|
||||
- toss fire grenade
|
||||
- throwing fire grenade
|
||||
- throw fire grenade
|
||||
- fire grenade throwing
|
||||
- fire grenade throw
|
||||
- tossing fire grenade
|
||||
- fire grenade tossing
|
||||
https://www.youtube.com/watch?v=9otQ9OLfaQc:
|
||||
- grenade out
|
||||
https://www.youtube.com/watch?v=tFA-8Vc32Kg:
|
||||
- famas
|
||||
https://www.youtube.com/watch?v=MdI1u8oXKZw:
|
||||
- awp zoom
|
||||
- zoom awp
|
||||
- awp scope
|
||||
- scope awp
|
||||
https://www.youtube.com/watch?v=6NiZhX4h32Q:
|
||||
- c4
|
||||
https://www.youtube.com/watch?v=3N0NxsyWPiY:
|
||||
- planting c4
|
||||
- c4 planting
|
||||
- plant c4
|
||||
- c4 plant
|
||||
https://www.youtube.com/watch?v=XLaJIXZ5QUc:
|
||||
- awp
|
||||
https://www.youtube.com/watch?v=DmuK9Wml88E:
|
||||
- P90
|
||||
https://www.youtube.com/watch?v=t1Ky_TbDXHY:
|
||||
- smoke
|
||||
https://www.youtube.com/watch?v=sJvdTbejDRY:
|
||||
- kill bonus
|
||||
https://www.youtube.com/watch?v=DYWi8qdvWCk:
|
||||
- AK47
|
||||
- AK 47
|
5183
audiotrivia/data/lists/games-plab.yaml
Normal file
5183
audiotrivia/data/lists/games-plab.yaml
Normal file
File diff suppressed because it is too large
Load Diff
304
audiotrivia/data/lists/games.yaml
Normal file
304
audiotrivia/data/lists/games.yaml
Normal file
@ -0,0 +1,304 @@
|
||||
AUTHOR: bobloy
|
||||
https://www.youtube.com/watch?v=FrceWR4XnVU:
|
||||
- shovel knight
|
||||
https://www.youtube.com/watch?v=Fn0khIn2wfc:
|
||||
- super mario world
|
||||
https://www.youtube.com/watch?v=qkYSuWSPkHI:
|
||||
- the legend of zelda
|
||||
- legend of zelda
|
||||
- zelda
|
||||
https://www.youtube.com/watch?v=0hvlwLwxweI:
|
||||
- dragon quest ix
|
||||
- dragon quest 9
|
||||
https://www.youtube.com/watch?v=GxrKe9z4CCo:
|
||||
- chrono trigger
|
||||
https://www.youtube.com/watch?v=pz3BQFXjEOI:
|
||||
- super smash bros melee
|
||||
- super smash bros. melee
|
||||
- super smash brothers melee
|
||||
https://www.youtube.com/watch?v=l_ioujmtqjg:
|
||||
- super mario bros
|
||||
- super mario brothers
|
||||
- super mario bros.
|
||||
https://www.youtube.com/watch?v=zTztR_y9iHc:
|
||||
- banjo-kazooie
|
||||
- banjo kazooie
|
||||
https://www.youtube.com/watch?v=6gWyfQFdMJA:
|
||||
- metroid samus returns
|
||||
https://www.youtube.com/watch?v=0jXTBAGv9ZQ:
|
||||
- halo
|
||||
https://www.youtube.com/watch?v=Rhaq4JP_t6o:
|
||||
- the elder scrolls iii morrowind
|
||||
- morrowind
|
||||
- elder scrolls iii
|
||||
- elder scrolls 3
|
||||
https://www.youtube.com/watch?v=ZksNhHyEhE0:
|
||||
- sonic generations
|
||||
https://www.youtube.com/watch?v=lndBgOrTWxo:
|
||||
- donkey kong country 2
|
||||
- donkey kong country two
|
||||
https://www.youtube.com/watch?v=uTEMsmLoEA4:
|
||||
- mario kart 8
|
||||
- mario kart eight
|
||||
https://www.youtube.com/watch?v=WA2WjP6sgrc:
|
||||
- donkey kong country tropical freeze
|
||||
- tropical freeze
|
||||
https://www.youtube.com/watch?v=9wMjq58Fjvo:
|
||||
- castle crashers
|
||||
https://www.youtube.com/watch?v=sr2nK06zZkg:
|
||||
- shadow of the colossus
|
||||
https://www.youtube.com/watch?v=6CMTXyExkeI:
|
||||
- final fantasy v
|
||||
- final fantasy 5
|
||||
https://www.youtube.com/watch?v=nRbROTdOgj0:
|
||||
- legend of zelda skyward sword
|
||||
- skyward sword
|
||||
https://www.youtube.com/watch?v=LFcH84oNU6s:
|
||||
- skies of arcadia
|
||||
https://www.youtube.com/watch?v=VEIWhy-urqM:
|
||||
- super mario galaxy
|
||||
https://www.youtube.com/watch?v=IT12DW2Fm9M:
|
||||
- final fantasy iv
|
||||
- final fantasy 4
|
||||
https://www.youtube.com/watch?v=UZbqrZJ9VA4:
|
||||
- mother3
|
||||
- mother 3
|
||||
https://www.youtube.com/watch?v=o_ayLF9vdls:
|
||||
- dragon age origins
|
||||
https://www.youtube.com/watch?v=eVVXNDv8rY0:
|
||||
- the elder scrolls v skyrim
|
||||
- elder scrolls v
|
||||
- elder scrolls 5
|
||||
- the elder scrolls 5 skyrim
|
||||
- skyrim
|
||||
https://www.youtube.com/watch?v=kzvZE4BY0hY:
|
||||
- fallout 4
|
||||
https://www.youtube.com/watch?v=VTsD2FjmLsw:
|
||||
- mass effect 2
|
||||
https://www.youtube.com/watch?v=800be1ZmGd0:
|
||||
- world of warcraft
|
||||
https://www.youtube.com/watch?v=SXKrsJZWqK0:
|
||||
- batman arkham city
|
||||
- arkham city
|
||||
https://www.youtube.com/watch?v=BLEBtvOhGnM:
|
||||
- god of war iii
|
||||
- god of war 3
|
||||
https://www.youtube.com/watch?v=rxgTlQLm4Xg:
|
||||
- gears of war 3
|
||||
https://www.youtube.com/watch?v=QiPon8lr48U:
|
||||
- metal gear solid 2
|
||||
https://www.youtube.com/watch?v=qDnaIfiH37w:
|
||||
- super smash bros wii u
|
||||
- super smash bros. wii u
|
||||
- super smash brothers wii u
|
||||
- super smash bros wiiu
|
||||
- super smash bros. wiiu
|
||||
- super smash brothers wiiu
|
||||
https://www.youtube.com/watch?v=_Uzlm2MaCWw:
|
||||
- mega man maverick hunter x
|
||||
- megaman maverick hunter x
|
||||
- maverick hunter x
|
||||
https://www.youtube.com/watch?v=-8wo0KBQ3oI:
|
||||
- doom
|
||||
https://www.youtube.com/watch?v=TN36CetQw6I:
|
||||
- super smash bros brawl
|
||||
- super smash bros. brawl
|
||||
- super smash brothers brawl
|
||||
https://www.youtube.com/watch?v=01IEjvD5lss:
|
||||
- guilty gear
|
||||
https://www.youtube.com/watch?v=VXX4Ft1I0Dw:
|
||||
- dynasty warriors 6
|
||||
https://www.youtube.com/watch?v=liRMh4LzQQU:
|
||||
- doom 2016
|
||||
- doom
|
||||
https://www.youtube.com/watch?v=ouw3jLAUXWE:
|
||||
- devil may cry 3
|
||||
https://www.youtube.com/watch?v=B_MW65XxS7s:
|
||||
- final fantasy vii
|
||||
- final fantasy 7
|
||||
https://www.youtube.com/watch?v=viM0-3PXef0:
|
||||
- the witcher 3
|
||||
- witcher 3
|
||||
https://www.youtube.com/watch?v=WQYN2P3E06s:
|
||||
- civilization vi
|
||||
- civilization 6
|
||||
https://www.youtube.com/watch?v=qOMQxVtbkik:
|
||||
- guild wars 2
|
||||
- guild wars two
|
||||
https://www.youtube.com/watch?v=WwHrQdC02FY:
|
||||
- final fantasy vi
|
||||
- final fantasy 6
|
||||
https://www.youtube.com/watch?v=2_wkJ377LzU:
|
||||
- journey
|
||||
https://www.youtube.com/watch?v=IJiHDmyhE1A:
|
||||
- civilization iv
|
||||
- civilization 4
|
||||
https://www.youtube.com/watch?v=kN_LvY97Rco:
|
||||
- ori and the blind forest
|
||||
https://www.youtube.com/watch?v=TO7UI0WIqVw:
|
||||
- super smash bros brawl
|
||||
- super smash bros. brawl
|
||||
- super smash brothers brawl
|
||||
https://www.youtube.com/watch?v=s9XljBWGrRQ:
|
||||
- kingdom hearts
|
||||
https://www.youtube.com/watch?v=xkolWbZdGbM:
|
||||
- shenmue
|
||||
https://www.youtube.com/watch?v=h-0G_FI61a8:
|
||||
- final fantasy x
|
||||
- final fantasy 10
|
||||
https://www.youtube.com/watch?v=do5NTPLMqXQ:
|
||||
- fire emblem fates
|
||||
https://www.youtube.com/watch?v=eFVj0Z6ahcI:
|
||||
- persona 5
|
||||
- persona five
|
||||
https://www.youtube.com/watch?v=PhciLj5VzOk:
|
||||
- super mario odyssey
|
||||
https://www.youtube.com/watch?v=GBPbJyxqHV0:
|
||||
- super mario 64
|
||||
- mario 64
|
||||
https://www.youtube.com/watch?v=wRWq53IFXVQ:
|
||||
- the legend of zelda the wind waker
|
||||
- legend of zelda the wind waker
|
||||
- the legend of zelda wind waker
|
||||
- legend of zelda wind waker
|
||||
- wind waker
|
||||
https://www.youtube.com/watch?v=nkPF5UiDi4g:
|
||||
- uncharted 2
|
||||
https://www.youtube.com/watch?v=CdYen5UII0s:
|
||||
- battlefield 1
|
||||
- battlefield one
|
||||
https://www.youtube.com/watch?v=8yj-25MOgOM:
|
||||
- star fox zero
|
||||
- starfox zero
|
||||
https://www.youtube.com/watch?v=Z9dNrmGD7mU:
|
||||
- dark souls iii
|
||||
- dark souls 3
|
||||
https://www.youtube.com/watch?v=Bio99hoZVYI:
|
||||
- fire emblem awakening
|
||||
https://www.youtube.com/watch?v=4EcgruWlXnQ:
|
||||
- monty on the run
|
||||
https://www.youtube.com/watch?v=oEf8gPFFZ58:
|
||||
- mega man 3
|
||||
- megaman 3
|
||||
https://www.youtube.com/watch?v=ifbr2NQ3Js0:
|
||||
- castlevania
|
||||
https://www.youtube.com/watch?v=W7rhEKTX-sE:
|
||||
- shovel knight
|
||||
https://www.youtube.com/watch?v=as_ct9tgkZA:
|
||||
- mega man 2
|
||||
- megaman 2
|
||||
https://www.youtube.com/watch?v=FB9Pym-sdbs:
|
||||
- actraiser
|
||||
https://www.youtube.com/watch?v=G3zhZHU6B2M:
|
||||
- ogre battle
|
||||
https://www.youtube.com/watch?v=hlrOAEr6dXc:
|
||||
- metroid zero mission
|
||||
- zero mission
|
||||
https://www.youtube.com/watch?v=jl6kjAkVw_s:
|
||||
- sonic 2
|
||||
https://www.youtube.com/watch?v=K8GRDNU50b8:
|
||||
- the legend of zelda ocarina of time
|
||||
- legend of zelda ocarina of time
|
||||
- ocarina of time
|
||||
https://www.youtube.com/watch?v=dTZ8uhJ5hIE:
|
||||
- kirby's epic yarn
|
||||
- kirbys epic yarn
|
||||
https://www.youtube.com/watch?v=QaaD9CnWgig:
|
||||
- super smash bros brawl
|
||||
- super smash bros. brawl
|
||||
- super smash brothers brawl
|
||||
https://www.youtube.com/watch?v=JDqJa1RC3q8:
|
||||
- kid icarus uprising
|
||||
https://www.youtube.com/watch?v=MQurUl4Snio:
|
||||
- punch-out!!
|
||||
- punch-out
|
||||
- punch out
|
||||
- punchout
|
||||
https://www.youtube.com/watch?v=vlz6qgahnYQ:
|
||||
- super street fighter 2 turbo
|
||||
- super street fighter two turbo
|
||||
- street fighter 2 turbo
|
||||
- street fighter two turbo
|
||||
https://www.youtube.com/watch?v=FBLp-3Rw_u0:
|
||||
- mario & luigi bowser's inside story
|
||||
- mario and luigi bowser's inside story
|
||||
- mario & luigi bowsers inside story
|
||||
- mario and luigi bowsers inside story
|
||||
- bowser's inside story
|
||||
- bowsers inside story
|
||||
https://www.youtube.com/watch?v=jqE8M2ZnFL8:
|
||||
- grand theft auto 4
|
||||
- grand theft auto four
|
||||
https://www.youtube.com/watch?v=GQZLEegUK74:
|
||||
- goldeneye 007
|
||||
- goldeneye
|
||||
https://www.youtube.com/watch?v=nCe7W1ajzIE:
|
||||
- tmnt iv turtles in time
|
||||
- tmnt iv
|
||||
- tmnt 4 turtles in time
|
||||
- tmnt 4
|
||||
- turtles in time
|
||||
https://www.youtube.com/watch?v=YHEifuLCSIY:
|
||||
- ducktales
|
||||
https://www.youtube.com/watch?v=rXefFHRgyE0:
|
||||
- pokemon diamond
|
||||
- pokemon pearl
|
||||
- pokemon platinum
|
||||
https://www.youtube.com/watch?v=4jaIUlz-wNU:
|
||||
- warriors orochi 3
|
||||
- warriors orochi three
|
||||
https://www.youtube.com/watch?v=EAwWPadFsOA:
|
||||
- mortal kombat
|
||||
https://www.youtube.com/watch?v=XI1VpElKWF8:
|
||||
- metal gear solid
|
||||
https://www.youtube.com/watch?v=zz8m1oEkW5k:
|
||||
- tetris blitz
|
||||
https://www.youtube.com/watch?v=gMdX_Iloow8:
|
||||
- ultimate marvel vs capcom 3
|
||||
- marvel vs capcom 3
|
||||
- ultimate marvel vs. capcom 3
|
||||
- marvel vs. capcom 3
|
||||
https://www.youtube.com/watch?v=vRe3h1iQ1Os:
|
||||
- sonic the hedgehog 2006
|
||||
- sonic the hegehog
|
||||
https://www.youtube.com/watch?v=SYTS2sJWcIs:
|
||||
- pokemon heartgold
|
||||
- pokemon soulsilver
|
||||
https://www.youtube.com/watch?v=5-BIqqSe1nU:
|
||||
- red dead redemption
|
||||
https://www.youtube.com/watch?v=wp6QpMWaKpE:
|
||||
- bioshock
|
||||
https://www.youtube.com/watch?v=R9XdMnsKvUs:
|
||||
- call of duty 4 modern warfare
|
||||
- call of duty 4
|
||||
- modern warfare
|
||||
https://www.youtube.com/watch?v=f-sQhBDsjgE:
|
||||
- killzone 2
|
||||
https://www.youtube.com/watch?v=-_O6F5FwQ0s:
|
||||
- soul calibur v
|
||||
- sould calibur 5
|
||||
https://www.youtube.com/watch?v=MgK_OfW7nl4:
|
||||
- the legend of zelda breath of the wild
|
||||
- legend of zelda breath of the wild
|
||||
- breath of the wild
|
||||
https://www.youtube.com/watch?v=tz82xbLvK_k:
|
||||
- undertale
|
||||
https://www.youtube.com/watch?v=J46RY4PU8a8:
|
||||
- chrono cross
|
||||
https://www.youtube.com/watch?v=6LB7LZZGpkw:
|
||||
- silent hill 2
|
||||
https://www.youtube.com/watch?v=ya3yxTbkh5s:
|
||||
- Ōkami
|
||||
- okami
|
||||
- wolf
|
||||
https://www.youtube.com/watch?v=KGidvt4NTPI:
|
||||
- hikari 光
|
||||
- hikari
|
||||
- 光
|
||||
- light
|
||||
https://www.youtube.com/watch?v=JbXVNKtmWnc:
|
||||
- final fantasy vi
|
||||
- final fantasy 6
|
||||
https://www.youtube.com/watch?v=-jMDutXA4-M:
|
||||
- final fantasy iii
|
||||
- final fantasy 3
|
4
audiotrivia/data/lists/guitar.yaml
Normal file
4
audiotrivia/data/lists/guitar.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
https://www.youtube.com/watch?v=hfyE220BsD0:
|
||||
- holiday
|
||||
https://www.youtube.com/watch?v=Hh3U9iPKeXQ:
|
||||
- sultans of swing
|
4
audiotrivia/data/lists/league.yaml
Normal file
4
audiotrivia/data/lists/league.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
https://www.youtube.com/watch?v=Hi1kUdreiWk:
|
||||
- Jinx
|
||||
https://www.youtube.com/watch?v=PNYHFluhOGI:
|
||||
- Teemo
|
20
audiotrivia/info.json
Normal file
20
audiotrivia/info.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Start an Audio Trivia game",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing Audio trivia!\n You **MUST** unload trivia to use this (`[p]unload trivia`)\n Then you can get started with `[p]load audiotrivia` and `[p]help AudioTrivia`",
|
||||
"requirements": [],
|
||||
"short": "Start an Audio Trivia game",
|
||||
"tags": [
|
||||
"fox",
|
||||
"bobloy",
|
||||
"games"
|
||||
]
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
import asyncio
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from redbot.core import Config, checks
|
||||
from redbot.core import commands
|
||||
from redbot.core.utils.chat_formatting import pagify, box
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
class CCRole:
|
||||
|
||||
class CCRole(Cog):
|
||||
"""
|
||||
Custom commands
|
||||
Creates commands used to display text and adjust roles
|
||||
@ -23,13 +26,14 @@ class CCRole:
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.group(no_pm=True)
|
||||
@commands.guild_only()
|
||||
@commands.group()
|
||||
async def ccrole(self, ctx):
|
||||
"""Custom commands management with roles
|
||||
|
||||
Highly customizable custom commands with role management."""
|
||||
if not ctx.invoked_subcommand:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@ccrole.command(name="add")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@ -105,7 +109,7 @@ class CCRole:
|
||||
return
|
||||
|
||||
# Selfrole
|
||||
await ctx.send('Is this a targeted command?(yes/no)\nNo will make this a selfrole command')
|
||||
await ctx.send('Is this a targeted command?(yes//no)\nNo will make this a selfrole command')
|
||||
|
||||
try:
|
||||
answer = await self.bot.wait_for('message', timeout=120, check=check)
|
||||
@ -190,7 +194,7 @@ class CCRole:
|
||||
"""Shows custom commands list"""
|
||||
guild = ctx.guild
|
||||
cmd_list = await self.config.guild(guild).cmdlist()
|
||||
cmd_list = {k: v for k,v in cmd_list.items() if v}
|
||||
cmd_list = {k: v for k, v in cmd_list.items() if v}
|
||||
if not cmd_list:
|
||||
await ctx.send(
|
||||
"There are no custom commands in this server. Use `{}ccrole add` to start adding some.".format(
|
||||
|
@ -1,10 +1,22 @@
|
||||
{
|
||||
"author" : ["Bobloy"],
|
||||
"bot_version" : [3,0,0],
|
||||
"description" : "[Incomplete] Creates custom commands to adjust roles and send custom messages",
|
||||
"hidden" : false,
|
||||
"install_msg" : "Thank you for installing Custom Commands w/ Roles.",
|
||||
"requirements" : [],
|
||||
"short" : "[Incomplete] Creates commands that adjust roles",
|
||||
"tags" : ["fox", "bobloy", "utility", "tools", "roles"]
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "[Incomplete] Creates custom commands to adjust roles and send custom messages",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing Custom Commands w/ Roles. Get started with `[p]load ccrole` and `[p]help CCRole`",
|
||||
"requirements": [],
|
||||
"short": "[Incomplete] Creates commands that adjust roles",
|
||||
"tags": [
|
||||
"fox",
|
||||
"bobloy",
|
||||
"utility",
|
||||
"tools",
|
||||
"roles"
|
||||
]
|
||||
}
|
@ -1,16 +1,22 @@
|
||||
import asyncio
|
||||
import pathlib
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from redbot.core import Config
|
||||
from redbot.core import commands
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
|
||||
from chatter.chatterbot import ChatBot
|
||||
from chatter.chatterbot.comparisons import levenshtein_distance
|
||||
from chatter.chatterbot.response_selection import get_first_response
|
||||
from chatter.chatterbot.trainers import ListTrainer
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
|
||||
class Chatter:
|
||||
class Chatter(Cog):
|
||||
"""
|
||||
This cog trains a chatbot that will talk like members of your Guild
|
||||
"""
|
||||
@ -23,11 +29,23 @@ class Chatter:
|
||||
"whitelist": None,
|
||||
"days": 1
|
||||
}
|
||||
path: pathlib.Path = cog_data_path(self)
|
||||
data_path = path / ("database.sqlite3")
|
||||
|
||||
self.chatbot = ChatBot(
|
||||
"ChatterBot",
|
||||
storage_adapter='chatter.chatterbot.storage.SQLStorageAdapter',
|
||||
database='./database.sqlite3'
|
||||
database=str(data_path),
|
||||
statement_comparison_function=levenshtein_distance,
|
||||
response_selection_method=get_first_response,
|
||||
logic_adapters=[
|
||||
'chatter.chatterbot.logic.BestMatch',
|
||||
{
|
||||
'import_path': 'chatter.chatterbot.logic.LowConfidenceAdapter',
|
||||
'threshold': 0.65,
|
||||
'default_response': ':thinking:'
|
||||
}
|
||||
]
|
||||
)
|
||||
self.chatbot.set_trainer(ListTrainer)
|
||||
|
||||
@ -42,21 +60,42 @@ class Chatter:
|
||||
Currently takes a stupid long time
|
||||
Returns a list of text
|
||||
"""
|
||||
out = []
|
||||
out = [[]]
|
||||
after = datetime.today() - timedelta(days=(await self.config.guild(ctx.guild).days()))
|
||||
|
||||
def new_message(msg, sent, out_in):
|
||||
if sent is None:
|
||||
return False
|
||||
|
||||
if len(out_in) < 2:
|
||||
return False
|
||||
|
||||
return msg.created_at - sent >= timedelta(hours=3) # This should be configurable perhaps
|
||||
|
||||
for channel in ctx.guild.text_channels:
|
||||
if in_channel:
|
||||
channel = in_channel
|
||||
await ctx.send("Gathering {}".format(channel.mention))
|
||||
user = None
|
||||
i = 0
|
||||
send_time = None
|
||||
try:
|
||||
|
||||
async for message in channel.history(limit=None, reverse=True, after=after):
|
||||
# if message.author.bot: # Skip bot messages
|
||||
# continue
|
||||
if new_message(message, send_time, out[i]):
|
||||
out.append([])
|
||||
i += 1
|
||||
user = None
|
||||
else:
|
||||
send_time = message.created_at + timedelta(seconds=1)
|
||||
if user == message.author:
|
||||
out[-1] += "\n" + message.clean_content
|
||||
out[i][-1] += "\n" + message.clean_content
|
||||
else:
|
||||
user = message.author
|
||||
out.append(message.clean_content)
|
||||
out[i].append(message.clean_content)
|
||||
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
@ -69,21 +108,22 @@ class Chatter:
|
||||
|
||||
def _train(self, data):
|
||||
try:
|
||||
self.chatbot.train(data)
|
||||
for convo in data:
|
||||
self.chatbot.train(convo)
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
@commands.group()
|
||||
async def chatter(self, ctx: RedContext):
|
||||
@commands.group(invoke_without_command=False)
|
||||
async def chatter(self, ctx: commands.Context):
|
||||
"""
|
||||
Base command for this cog. Check help for the commands list.
|
||||
"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@chatter.command()
|
||||
async def age(self, ctx: RedContext, days: int):
|
||||
async def age(self, ctx: commands.Context, days: int):
|
||||
"""
|
||||
Sets the number of days to look back
|
||||
Will train on 1 day otherwise
|
||||
@ -98,7 +138,8 @@ class Chatter:
|
||||
Backup your training data to a json for later use
|
||||
"""
|
||||
await ctx.send("Backing up data, this may take a while")
|
||||
future = await self.loop.run_in_executor(None, self.chatbot.trainer.export_for_training, './{}.json'.format(backupname))
|
||||
future = await self.loop.run_in_executor(None, self.chatbot.trainer.export_for_training,
|
||||
'./{}.json'.format(backupname))
|
||||
|
||||
if future:
|
||||
await ctx.send("Backup successful!")
|
||||
@ -133,17 +174,21 @@ class Chatter:
|
||||
else:
|
||||
await ctx.send("Error occurred :(")
|
||||
|
||||
async def on_message(self, message):
|
||||
async def on_message(self, message: discord.Message):
|
||||
"""
|
||||
Credit to https://github.com/Twentysix26/26-Cogs/blob/master/cleverbot/cleverbot.py
|
||||
for on_message recognition of @bot
|
||||
"""
|
||||
author = message.author
|
||||
channel = message.channel
|
||||
try:
|
||||
guild: discord.Guild = message.guild
|
||||
except AttributeError: # Not a guild message
|
||||
return
|
||||
|
||||
channel: discord.TextChannel = message.channel
|
||||
|
||||
if message.author.id != self.bot.user.id:
|
||||
to_strip = "@" + author.guild.me.display_name + " "
|
||||
if author.id != self.bot.user.id:
|
||||
to_strip = "@" + guild.me.display_name + " "
|
||||
text = message.clean_content
|
||||
if not text.startswith(to_strip):
|
||||
return
|
||||
@ -151,7 +196,7 @@ class Chatter:
|
||||
async with channel.typing():
|
||||
future = await self.loop.run_in_executor(None, self.chatbot.get_response, text)
|
||||
|
||||
if future:
|
||||
if future and str(future):
|
||||
await channel.send(str(future))
|
||||
else:
|
||||
await channel.send(':thinking:')
|
||||
|
@ -2,10 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from . import utils
|
||||
from .input import InputAdapter
|
||||
from .output import OutputAdapter
|
||||
from .storage import StorageAdapter
|
||||
from chatter.chatterbot import utils
|
||||
|
||||
|
||||
class ChatBot(object):
|
||||
@ -14,7 +11,7 @@ class ChatBot(object):
|
||||
"""
|
||||
|
||||
def __init__(self, name, **kwargs):
|
||||
from .logic import MultiLogicAdapter
|
||||
from chatter.chatterbot.logic import MultiLogicAdapter
|
||||
|
||||
self.name = name
|
||||
kwargs['name'] = name
|
||||
@ -33,9 +30,9 @@ class ChatBot(object):
|
||||
output_adapter = kwargs.get('output_adapter', 'chatter.chatterbot.output.OutputAdapter')
|
||||
|
||||
# Check that each adapter is a valid subclass of it's respective parent
|
||||
utils.validate_adapter_class(storage_adapter, StorageAdapter)
|
||||
utils.validate_adapter_class(input_adapter, InputAdapter)
|
||||
utils.validate_adapter_class(output_adapter, OutputAdapter)
|
||||
# utils.validate_adapter_class(storage_adapter, StorageAdapter)
|
||||
# utils.validate_adapter_class(input_adapter, InputAdapter)
|
||||
# utils.validate_adapter_class(output_adapter, OutputAdapter)
|
||||
|
||||
self.logic = MultiLogicAdapter(**kwargs)
|
||||
self.storage = utils.initialize_class(storage_adapter, **kwargs)
|
||||
@ -139,7 +136,7 @@ class ChatBot(object):
|
||||
"""
|
||||
Learn that the statement provided is a valid response.
|
||||
"""
|
||||
from .conversation import Response
|
||||
from chatter.chatterbot.conversation import Response
|
||||
|
||||
if previous_statement:
|
||||
statement.add_response(
|
||||
|
@ -92,7 +92,7 @@ class SynsetDistance(Comparator):
|
||||
"""
|
||||
Download required NLTK corpora if they have not already been downloaded.
|
||||
"""
|
||||
from .utils import nltk_download_corpus
|
||||
from chatter.chatterbot.utils import nltk_download_corpus
|
||||
|
||||
nltk_download_corpus('corpora/wordnet')
|
||||
|
||||
@ -100,7 +100,7 @@ class SynsetDistance(Comparator):
|
||||
"""
|
||||
Download required NLTK corpora if they have not already been downloaded.
|
||||
"""
|
||||
from .utils import nltk_download_corpus
|
||||
from chatter.chatterbot.utils import nltk_download_corpus
|
||||
|
||||
nltk_download_corpus('tokenizers/punkt')
|
||||
|
||||
@ -108,7 +108,7 @@ class SynsetDistance(Comparator):
|
||||
"""
|
||||
Download required NLTK corpora if they have not already been downloaded.
|
||||
"""
|
||||
from .utils import nltk_download_corpus
|
||||
from chatter.chatterbot.utils import nltk_download_corpus
|
||||
|
||||
nltk_download_corpus('corpora/stopwords')
|
||||
|
||||
@ -177,7 +177,7 @@ class SentimentComparison(Comparator):
|
||||
Download the NLTK vader lexicon for sentiment analysis
|
||||
that is required for this algorithm to run.
|
||||
"""
|
||||
from .utils import nltk_download_corpus
|
||||
from chatter.chatterbot.utils import nltk_download_corpus
|
||||
|
||||
nltk_download_corpus('sentiment/vader_lexicon')
|
||||
|
||||
@ -252,7 +252,7 @@ class JaccardSimilarity(Comparator):
|
||||
Download the NLTK wordnet corpora that is required for this algorithm
|
||||
to run only if the corpora has not already been downloaded.
|
||||
"""
|
||||
from .utils import nltk_download_corpus
|
||||
from chatter.chatterbot.utils import nltk_download_corpus
|
||||
|
||||
nltk_download_corpus('corpora/wordnet')
|
||||
|
||||
|
@ -3,6 +3,7 @@ class StatementMixin(object):
|
||||
This class has shared methods used to
|
||||
normalize different statement models.
|
||||
"""
|
||||
tags = []
|
||||
|
||||
def get_tags(self):
|
||||
"""
|
||||
@ -148,11 +149,7 @@ class Statement(StatementMixin):
|
||||
:returns: A dictionary representation of the statement object.
|
||||
:rtype: dict
|
||||
"""
|
||||
data = {}
|
||||
|
||||
data['text'] = self.text
|
||||
data['in_response_to'] = []
|
||||
data['extra_data'] = self.extra_data
|
||||
data = {'text': self.text, 'in_response_to': [], 'extra_data': self.extra_data}
|
||||
|
||||
for response in self.in_response_to:
|
||||
data['in_response_to'].append(response.serialize())
|
||||
@ -211,11 +208,6 @@ class Response(object):
|
||||
return self.text == other
|
||||
|
||||
def serialize(self):
|
||||
data = {}
|
||||
|
||||
data['text'] = self.text
|
||||
data['created_at'] = self.created_at.isoformat()
|
||||
|
||||
data['occurrence'] = self.occurrence
|
||||
data = {'text': self.text, 'created_at': self.created_at.isoformat(), 'occurrence': self.occurrence}
|
||||
|
||||
return data
|
||||
|
@ -1,5 +1,5 @@
|
||||
from .best_match import BestMatch
|
||||
from .logic_adapter import LogicAdapter
|
||||
from .best_match import BestMatch
|
||||
from .low_confidence import LowConfidenceAdapter
|
||||
from .mathematical_evaluation import MathematicalEvaluation
|
||||
from .multi_adapter import MultiLogicAdapter
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .logic_adapter import LogicAdapter
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class BestMatch(LogicAdapter):
|
||||
|
@ -1,7 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from .best_match import BestMatch
|
||||
from chatter.chatterbot.logic import BestMatch
|
||||
|
||||
|
||||
class LowConfidenceAdapter(BestMatch):
|
||||
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
from collections import Counter
|
||||
|
||||
from chatter.chatterbot import utils
|
||||
from .logic_adapter import LogicAdapter
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class MultiLogicAdapter(LogicAdapter):
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .logic_adapter import LogicAdapter
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class NoKnowledgeAdapter(LogicAdapter):
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .logic_adapter import LogicAdapter
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class SpecificResponseAdapter(LogicAdapter):
|
||||
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from .logic_adapter import LogicAdapter
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class TimeLogicAdapter(LogicAdapter):
|
||||
|
@ -1,8 +1,8 @@
|
||||
from .output_adapter import OutputAdapter
|
||||
from .gitter import Gitter
|
||||
from .hipchat import HipChat
|
||||
from .mailgun import Mailgun
|
||||
from .microsoft import Microsoft
|
||||
from .output_adapter import OutputAdapter
|
||||
from .terminal import TerminalAdapter
|
||||
|
||||
__all__ = (
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .output_adapter import OutputAdapter
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class Gitter(OutputAdapter):
|
||||
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from .output_adapter import OutputAdapter
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class HipChat(OutputAdapter):
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .output_adapter import OutputAdapter
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class Mailgun(OutputAdapter):
|
||||
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from .output_adapter import OutputAdapter
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class Microsoft(OutputAdapter):
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .output_adapter import OutputAdapter
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class TerminalAdapter(OutputAdapter):
|
||||
|
@ -611,6 +611,7 @@ def date_from_duration(base_date, number_as_string, unit, duration, base_time=No
|
||||
if base_time is not None:
|
||||
base_date = date_from_adverb(base_date, base_time)
|
||||
num = convert_string_to_number(number_as_string)
|
||||
args = {}
|
||||
if unit in day_variations:
|
||||
args = {'days': num}
|
||||
elif unit in minute_variations:
|
||||
|
@ -183,7 +183,7 @@ class SQLStorageAdapter(StorageAdapter):
|
||||
if isinstance(_filter, list):
|
||||
if len(_filter) == 0:
|
||||
_query = _response_query.filter(
|
||||
Statement.in_response_to == None # NOQA Here must use == instead of is
|
||||
Statement.in_response_to is None # NOQA Here must use == instead of is
|
||||
)
|
||||
else:
|
||||
for f in _filter:
|
||||
@ -193,7 +193,7 @@ class SQLStorageAdapter(StorageAdapter):
|
||||
if fp == 'in_response_to__contains':
|
||||
_query = _response_query.join(Response).filter(Response.text == _filter)
|
||||
else:
|
||||
_query = _response_query.filter(Statement.in_response_to == None) # NOQA
|
||||
_query = _response_query.filter(Statement.in_response_to is None) # NOQA
|
||||
else:
|
||||
if _query:
|
||||
_query = _query.filter(Response.statement_text.like('%' + _filter + '%'))
|
||||
|
@ -158,7 +158,9 @@ class StorageAdapter(object):
|
||||
class EmptyDatabaseException(Exception):
|
||||
|
||||
def __init__(self,
|
||||
value='The database currently contains no entries. At least one entry is expected. You may need to train your chat bot to populate your database.'):
|
||||
value='The database currently contains no entries. '
|
||||
'At least one entry is expected. '
|
||||
'You may need to train your chat bot to populate your database.'):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
|
@ -2,8 +2,8 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from . import utils
|
||||
from .conversation import Statement, Response
|
||||
from chatter.chatterbot import utils
|
||||
from chatter.chatterbot.conversation import Statement, Response
|
||||
|
||||
|
||||
class Trainer(object):
|
||||
@ -127,7 +127,7 @@ class ChatterBotCorpusTrainer(Trainer):
|
||||
|
||||
def __init__(self, storage, **kwargs):
|
||||
super(ChatterBotCorpusTrainer, self).__init__(storage, **kwargs)
|
||||
from .corpus import Corpus
|
||||
from chatter.chatterbot.corpus import Corpus
|
||||
|
||||
self.corpus = Corpus()
|
||||
|
||||
@ -225,7 +225,7 @@ class TwitterTrainer(Trainer):
|
||||
|
||||
for word in tweet_words:
|
||||
# If the word contains only letters with a length from 4 to 9
|
||||
if word.isalpha() and len(word) > 3 and len(word) <= 9:
|
||||
if word.isalpha() and 3 < len(word) <= 9:
|
||||
words.add(word)
|
||||
|
||||
return words
|
||||
|
@ -46,7 +46,7 @@ def validate_adapter_class(validate_class, adapter_class):
|
||||
|
||||
:raises: Adapter.InvalidAdapterTypeException
|
||||
"""
|
||||
from .adapters import Adapter
|
||||
from chatter.chatterbot.adapters import Adapter
|
||||
|
||||
# If a dictionary was passed in, check if it has an import_path attribute
|
||||
if isinstance(validate_class, dict):
|
||||
|
@ -9,7 +9,7 @@
|
||||
],
|
||||
"description": "Create an offline chatbot that talks like your average member using Machine Learning",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing Chatter!",
|
||||
"install_msg": "Thank you for installing Chatter! Get started ith `[p]load chatter` and `[p]help Chatter`",
|
||||
"requirements": [
|
||||
"sqlalchemy<1.3,>=1.2",
|
||||
"python-twitter<4.0,>=3.0",
|
||||
|
5
coglint/__init__.py
Normal file
5
coglint/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .coglint import CogLint
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(CogLint(bot))
|
89
coglint/coglint.py
Normal file
89
coglint/coglint.py
Normal file
@ -0,0 +1,89 @@
|
||||
import discord
|
||||
from pylint import epylint as lint
|
||||
from redbot.core import Config
|
||||
from redbot.core import commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class CogLint(Cog):
|
||||
"""
|
||||
Automatically lint code in python codeblocks
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_global = {
|
||||
"lint": True
|
||||
}
|
||||
default_guild = {}
|
||||
|
||||
self.path = str(cog_data_path(self)).replace('\\', '/')
|
||||
|
||||
self.do_lint = None
|
||||
self.counter = 0
|
||||
|
||||
# self.answer_path = self.path + "/tmpfile.py"
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.command()
|
||||
async def autolint(self, ctx: commands.Context):
|
||||
"""Toggles automatically linting code"""
|
||||
curr = await self.config.lint()
|
||||
|
||||
self.do_lint = not curr
|
||||
await self.config.lint.set(not curr)
|
||||
await ctx.send("Autolinting is now set to {}".format(not curr))
|
||||
|
||||
@commands.command()
|
||||
async def lint(self, ctx: commands.Context, *, code):
|
||||
"""Lint python code
|
||||
|
||||
Toggle autolinting with `[p]autolint`
|
||||
"""
|
||||
await self.lint_message(ctx.message)
|
||||
await ctx.send("Hello World")
|
||||
|
||||
async def lint_code(self, code):
|
||||
self.counter += 1
|
||||
path = self.path + "/{}.py".format(self.counter)
|
||||
with open(path, 'w') as codefile:
|
||||
codefile.write(code)
|
||||
|
||||
future = await self.bot.loop.run_in_executor(None, lint.py_run, path, 'return_std=True')
|
||||
|
||||
if future:
|
||||
(pylint_stdout, pylint_stderr) = future
|
||||
else:
|
||||
(pylint_stdout, pylint_stderr) = None, None
|
||||
|
||||
# print(pylint_stderr)
|
||||
# print(pylint_stdout)
|
||||
|
||||
return pylint_stdout, pylint_stderr
|
||||
|
||||
async def lint_message(self, message):
|
||||
if self.do_lint is None:
|
||||
self.do_lint = await self.config.lint()
|
||||
if not self.do_lint:
|
||||
return
|
||||
code_blocks = message.content.split('```')[1::2]
|
||||
|
||||
for c in code_blocks:
|
||||
is_python, code = c.split(None, 1)
|
||||
is_python = is_python.lower() == 'python'
|
||||
if is_python: # Then we're in business
|
||||
linted, errors = await self.lint_code(code)
|
||||
linted = linted.getvalue()
|
||||
errors = errors.getvalue()
|
||||
await message.channel.send(linted)
|
||||
# await message.channel.send(errors)
|
||||
|
||||
async def on_message(self, message: discord.Message):
|
||||
await self.lint_message(message)
|
20
coglint/info..json
Normal file
20
coglint/info..json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Lint python code posted in chat",
|
||||
"hidden": true,
|
||||
"install_msg": "Thank you for installing CogLint! Get started with `[p]load coglint` and `[p]help CogLint`",
|
||||
"requirements": [],
|
||||
"short": "Python cog linter",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"utils",
|
||||
"tools"
|
||||
]
|
||||
}
|
5
dad/__init__.py
Normal file
5
dad/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .dad import Dad
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Dad(bot))
|
112
dad/dad.py
Normal file
112
dad/dad.py
Normal file
@ -0,0 +1,112 @@
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
import discord
|
||||
from redbot.core import Config, checks
|
||||
from redbot.core import commands
|
||||
from redbot.core.bot import Red
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
async def fetch_url(session, url):
|
||||
async with session.get(url) as response:
|
||||
assert response.status == 200
|
||||
return await response.json()
|
||||
|
||||
|
||||
class Dad(Cog):
|
||||
"""
|
||||
Dad jokes
|
||||
|
||||
Nicknaming user idea comes from https://github.com/Vexs/DadBot
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=6897100, force_registration=True)
|
||||
|
||||
default_guild = {
|
||||
"enabled": False,
|
||||
"nickname": False,
|
||||
"cooldown": 240
|
||||
}
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
self.cooldown = defaultdict(datetime.now)
|
||||
|
||||
@commands.command()
|
||||
async def dadjoke(self, ctx: commands.Context):
|
||||
headers = {"User-Agent": "FoxV3 (https://github.com/bobloy/Fox-V3)",
|
||||
"Accept": "application/json"}
|
||||
|
||||
async with aiohttp.ClientSession(headers=headers) as session:
|
||||
joke = await fetch_url(session, 'https://icanhazdadjoke.com/')
|
||||
|
||||
em = discord.Embed()
|
||||
em.set_image(url="https://icanhazdadjoke.com/j/{}.png".format(joke['id']))
|
||||
|
||||
await ctx.send(embed=em)
|
||||
|
||||
@commands.group()
|
||||
@checks.admin()
|
||||
async def dad(self, ctx: commands.Context):
|
||||
"""Dad joke superhub"""
|
||||
pass
|
||||
|
||||
@dad.command(name='toggle')
|
||||
async def dad_toggle(self, ctx: commands.Context):
|
||||
"""Toggle automatic dad jokes on or off"""
|
||||
is_on = await self.config.guild(ctx.guild).enabled()
|
||||
await self.config.guild(ctx.guild).enabled.set(not is_on)
|
||||
await ctx.send("Auto dad jokes are now set to {}".format(not is_on))
|
||||
|
||||
@dad.command(name='nickname')
|
||||
async def dad_nickname(self, ctx: commands.Context):
|
||||
"""Toggle nicknaming"""
|
||||
is_on = await self.config.guild(ctx.guild).nickname()
|
||||
await self.config.guild(ctx.guild).nickname.set(not is_on)
|
||||
await ctx.send("Nicknaming is now set to {}".format(not is_on))
|
||||
|
||||
@dad.command(name='cooldown')
|
||||
async def dad_cooldown(self, ctx: commands.Context, cooldown: int):
|
||||
"""Set the auto-joke cooldown"""
|
||||
|
||||
await self.config.guild(ctx.guild).cooldown.set(cooldown)
|
||||
await ctx.send("Dad joke cooldown is now set to {}".format(cooldown))
|
||||
|
||||
async def on_message(self, message: discord.Message):
|
||||
guild: discord.Guild = message.guild
|
||||
if guild is None:
|
||||
return
|
||||
|
||||
guild_config = self.config.guild(guild)
|
||||
is_on = await guild_config.enabled()
|
||||
if not is_on:
|
||||
return
|
||||
|
||||
if self.cooldown[guild.id] > datetime.now():
|
||||
return
|
||||
|
||||
lower = message.clean_content.lower()
|
||||
lower_split = lower.split()
|
||||
if len(lower_split)==0:
|
||||
return
|
||||
|
||||
if lower_split[0] == "i'm" and len(lower_split) >= 2:
|
||||
if await guild_config.nickname():
|
||||
try:
|
||||
await message.author.edit(nick=lower[4:])
|
||||
except discord.Forbidden:
|
||||
out = lower[4:]
|
||||
else:
|
||||
out = message.author.mention
|
||||
else:
|
||||
out = lower[4:]
|
||||
|
||||
await message.channel.send("Hi {}, I'm {}!".format(out, guild.me.display_name))
|
||||
|
||||
self.cooldown[guild.id] = datetime.now() + timedelta(seconds=(await guild_config.cooldown()))
|
20
dad/info..json
Normal file
20
dad/info..json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Tell dad jokes and give out bad nicknames",
|
||||
"hidden": true,
|
||||
"install_msg": "Thank you for installing Dad. Get started with `[p]load dad`, then `[p]help Dad`",
|
||||
"requirements": [],
|
||||
"short": "Dad joke bot",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"utils",
|
||||
"tools"
|
||||
]
|
||||
}
|
5
exclusiverole/__init__.py
Normal file
5
exclusiverole/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .exclusiverole import ExclusiveRole
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(ExclusiveRole(bot))
|
92
exclusiverole/exclusiverole.py
Normal file
92
exclusiverole/exclusiverole.py
Normal file
@ -0,0 +1,92 @@
|
||||
import asyncio
|
||||
|
||||
import discord
|
||||
from redbot.core import Config, checks, commands
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class ExclusiveRole(Cog):
|
||||
"""
|
||||
Custom commands
|
||||
Creates commands used to display text and adjust roles
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=9999114111108101)
|
||||
default_guild = {
|
||||
"role_list": []
|
||||
}
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.group(no_pm=True, aliases=["exclusiverole"])
|
||||
async def exclusive(self, ctx):
|
||||
"""Base command for managing exclusive roles"""
|
||||
|
||||
if not ctx.invoked_subcommand:
|
||||
pass
|
||||
|
||||
@exclusive.command(name="add")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def exclusive_add(self, ctx, role: discord.Role):
|
||||
"""Adds an exclusive role"""
|
||||
if role.id in (await self.config.guild(ctx.guild).role_list()):
|
||||
await ctx.send("That role is already exclusive")
|
||||
return
|
||||
|
||||
async with self.config.guild(ctx.guild).role_list() as rl:
|
||||
rl.append(role.id)
|
||||
|
||||
await self.check_guild(ctx.guild)
|
||||
|
||||
await ctx.send("Exclusive role added")
|
||||
|
||||
@exclusive.command(name="delete")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def exclusive_delete(self, ctx, role: discord.Role):
|
||||
"""Deletes an exclusive role"""
|
||||
if role.id not in (await self.config.guild(ctx.guild).role_list()):
|
||||
await ctx.send("That role is not exclusive")
|
||||
return
|
||||
|
||||
async with self.config.guild(ctx.guild).role_list() as rl:
|
||||
rl.remove(role.id)
|
||||
|
||||
await ctx.send("Exclusive role removed")
|
||||
|
||||
async def check_guild(self, guild: discord.Guild):
|
||||
role_set = set(await self.config.guild(guild).role_list())
|
||||
for member in guild.members:
|
||||
try:
|
||||
await self.remove_non_exclusive_roles(member, role_set=role_set)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
|
||||
async def remove_non_exclusive_roles(self, member: discord.Member, role_set=None):
|
||||
if role_set is None:
|
||||
role_set = set(await self.config.guild(member.guild).role_list())
|
||||
|
||||
member_set = set([role.id for role in member.roles])
|
||||
to_remove = (member_set - role_set) - {member.guild.default_role.id}
|
||||
|
||||
if to_remove and member_set & role_set:
|
||||
to_remove = [discord.utils.get(member.guild.roles, id=id) for id in to_remove]
|
||||
await member.remove_roles(*to_remove, reason="Exclusive roles")
|
||||
|
||||
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
||||
if before.roles == after.roles:
|
||||
return
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
role_set = set(await self.config.guild(after.guild).role_list())
|
||||
member_set = set([role.id for role in after.roles])
|
||||
|
||||
if role_set & member_set:
|
||||
try:
|
||||
await self.remove_non_exclusive_roles(after, role_set=role_set)
|
||||
except discord.Forbidden:
|
||||
pass
|
22
exclusiverole/info.json
Normal file
22
exclusiverole/info.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Assign roles to be exclusive, preventing other roles from being added",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing ExclusiveRole. Get started with `[p]load exclusiverole` and `[p]help ExclusiveRole`",
|
||||
"requirements": [],
|
||||
"short": "Set roles to be exclusive",
|
||||
"tags": [
|
||||
"fox",
|
||||
"bobloy",
|
||||
"utility",
|
||||
"tools",
|
||||
"roles"
|
||||
]
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
import asyncio
|
||||
import os
|
||||
import math
|
||||
|
||||
# from typing import Union
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from redbot.core import commands
|
||||
|
||||
from redbot.core.utils.chat_formatting import pagify
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
@ -102,7 +103,7 @@ class Fight:
|
||||
await ctx.send("Current tournament ID: " + await self._activefight(ctx))
|
||||
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
# await ctx.send("I can do stuff!")
|
||||
|
||||
@fight.command(name="join")
|
||||
@ -198,10 +199,10 @@ class Fight:
|
||||
async def fadmin(self, ctx):
|
||||
"""Admin command for managing the current tournament"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@fadmin.command(name="score")
|
||||
async def fadmin_score(self, ctx, mID, score1, score2):
|
||||
async def fadmin_score(self, ctx: commands.Context, mID, score1, score2):
|
||||
"""Set's the score for matchID and clears disputes"""
|
||||
currFight = await self._getcurrentfight(ctx)
|
||||
tID = await self._activefight(ctx)
|
||||
@ -213,11 +214,11 @@ class Fight:
|
||||
await ctx.send("Tournament currently not accepting new players")
|
||||
return
|
||||
|
||||
if await self._infight(ctx, tID, user.id):
|
||||
if await self._infight(ctx, tID, ctx.user.id):
|
||||
await ctx.send("You are already in this tournament!")
|
||||
return
|
||||
|
||||
currFight["PLAYERS"].append(user.id)
|
||||
currFight["PLAYERS"].append(ctx.user.id)
|
||||
|
||||
await self._save_fight(ctx, tID, currFight)
|
||||
|
||||
@ -256,7 +257,7 @@ class Fight:
|
||||
# self.save_data()
|
||||
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
# await ctx.send("I can do stuff!")
|
||||
|
||||
@fightset.command(name="emoji")
|
||||
@ -548,7 +549,7 @@ class Fight:
|
||||
async def fightset_guild(self, ctx):
|
||||
"""Adjust guild wide settings"""
|
||||
if ctx.invoked_subcommand is None or isinstance(ctx.invoked_subcommand, commands.Group):
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@fightset_guild.command(name="selfreport")
|
||||
async def fightset_guild_selfreport(self, ctx):
|
||||
@ -711,11 +712,13 @@ class Fight:
|
||||
|
||||
async def _embed_tourney(self, ctx, tID):
|
||||
"""Prints a pretty embed of the tournament"""
|
||||
await ctx.send("_placeholder Todo")
|
||||
#_placeholder Todo
|
||||
pass
|
||||
|
||||
async def _comparescores(self):
|
||||
"""Checks user submitted scores for inconsistancies"""
|
||||
await ctx.send("_comparescores Todo")
|
||||
# _comparescores Todo
|
||||
pass
|
||||
|
||||
async def _parseuser(self, guild: discord.Guild, tID, userid):
|
||||
"""Finds user in the tournament"""
|
||||
@ -821,8 +824,8 @@ class Fight:
|
||||
"""Reports a win for member in match"""
|
||||
theT = await self._getfight(guild, tID)
|
||||
|
||||
if member.id not in theT["PLAYERS"]: # Shouldn't happen
|
||||
return False
|
||||
# if member.id not in theT["PLAYERS"]: # Shouldn't happen
|
||||
# return False
|
||||
|
||||
if theT["RULES"]["TYPE"] == 0:
|
||||
return await self._rr_report_dispute(guild, tID, mID)
|
||||
@ -833,13 +836,16 @@ class Fight:
|
||||
|
||||
# **********************Single Elimination***************************
|
||||
async def _elim_setup(self, tID):
|
||||
await ctx.send("Elim setup todo")
|
||||
# ToDo Elim setup
|
||||
pass
|
||||
|
||||
async def _elim_start(self, tID):
|
||||
await ctx.send("Elim start todo")
|
||||
# ToDo Elim start
|
||||
pass
|
||||
|
||||
async def _elim_update(self, matchID):
|
||||
await ctx.send("Elim update todo")
|
||||
# ToDo Elim update
|
||||
pass
|
||||
|
||||
# **********************Round-Robin**********************************
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"author" : ["Bobloy"],
|
||||
"bot_version" : [3,0,0],
|
||||
"description" : "[Incomplete] Cog to organize tournaments within Discord",
|
||||
"hidden" : false,
|
||||
"hidden" : true,
|
||||
"install_msg" : "Thank you for installing Fight. Run with [p]fight or [p]fightset",
|
||||
"requirements" : [],
|
||||
"short" : "[Incomplete] Cog to organize tournaments",
|
||||
|
5
flag/__init__.py
Normal file
5
flag/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .flag import Flag
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Flag(bot))
|
191
flag/flag.py
Normal file
191
flag/flag.py
Normal file
@ -0,0 +1,191 @@
|
||||
from datetime import date, timedelta
|
||||
|
||||
import discord
|
||||
from redbot.core import Config, checks, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.utils.chat_formatting import pagify
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class Flag(Cog):
|
||||
"""
|
||||
Set expiring flags on members
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_global = {}
|
||||
default_guild = {
|
||||
"days": 31,
|
||||
"dm": True,
|
||||
"flags": {}
|
||||
}
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@checks.is_owner()
|
||||
@commands.guild_only()
|
||||
@commands.command()
|
||||
async def clearallflag(self, ctx: commands.Context):
|
||||
"""Clears all flags for all members in this server"""
|
||||
|
||||
await self.config.guild(ctx.guild).flags.clear()
|
||||
await ctx.send("Done")
|
||||
|
||||
@checks.mod_or_permissions(manage_roles=True)
|
||||
@commands.guild_only()
|
||||
@commands.group()
|
||||
async def flagset(self, ctx: commands.Context):
|
||||
"""
|
||||
My custom cog
|
||||
|
||||
Extra information goes here
|
||||
"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
pass
|
||||
|
||||
@flagset.command(name="expire")
|
||||
async def flagset_expire(self, ctx: commands.Context, days: int):
|
||||
"""
|
||||
Set the number of days for flags to expire after for server
|
||||
"""
|
||||
await self.config.guild(ctx.guild).days.set(days)
|
||||
await ctx.send("Number of days for new flags to expire is now {} days".format(days))
|
||||
|
||||
@flagset.command(name="dm")
|
||||
async def flagset_dm(self, ctx: commands.Context):
|
||||
"""Toggles DM-ing the flags"""
|
||||
|
||||
dm = await self.config.guild(ctx.guild).dm()
|
||||
await self.config.guild(ctx.guild).dm.set(not dm)
|
||||
|
||||
await ctx.send("DM-ing members when they get a flag is now set to **{}**".format(not dm))
|
||||
|
||||
@staticmethod
|
||||
def _flag_template():
|
||||
return {
|
||||
'reason': "",
|
||||
'expireyear': 0,
|
||||
'expiremonth': 0,
|
||||
'expireday': 0
|
||||
}
|
||||
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(manage_roles=True)
|
||||
@commands.command()
|
||||
async def flag(self, ctx: commands.Context, member: discord.Member, *, reason):
|
||||
"""Flag a member"""
|
||||
guild = ctx.guild
|
||||
await self._check_flags(guild)
|
||||
# clashroyale = self.bot.get_cog('clashroyale')
|
||||
# if clashroyale is None:
|
||||
# await ctx.send("Requires clashroyale cog installed")
|
||||
# return
|
||||
|
||||
flag = self._flag_template()
|
||||
expiredate = date.today()
|
||||
expiredate += timedelta(days=await self.config.guild(guild).days())
|
||||
|
||||
flag['reason'] = reason
|
||||
flag['expireyear'] = expiredate.year
|
||||
flag['expiremonth'] = expiredate.month
|
||||
flag['expireday'] = expiredate.day
|
||||
|
||||
# flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[])
|
||||
# flags.append(flag)
|
||||
# await self.config.guild(guild).flags.set_raw(str(member.id), value=flags)
|
||||
|
||||
async with self.config.guild(guild).flags() as flags:
|
||||
flags[str(member.id)].append(flag)
|
||||
|
||||
outembed = await self._list_flags(member)
|
||||
|
||||
if outembed:
|
||||
await ctx.send(embed=outembed)
|
||||
if await self.config.guild(guild).dm():
|
||||
await member.send(embed=outembed)
|
||||
else:
|
||||
await ctx.send("This member has no flags.. somehow..")
|
||||
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(manage_roles=True)
|
||||
@commands.command(aliases=['flagclear'])
|
||||
async def clearflag(self, ctx: commands.Context, member: discord.Member):
|
||||
"""Clears flags for a member"""
|
||||
guild = ctx.guild
|
||||
await self._check_flags(guild)
|
||||
|
||||
await self.config.guild(guild).flags.set_raw(str(member.id), value=[])
|
||||
|
||||
await ctx.send("Success!")
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command(aliases=['flaglist'])
|
||||
async def listflag(self, ctx: commands.Context, member: discord.Member):
|
||||
"""Lists flags for a member"""
|
||||
server = ctx.guild
|
||||
await self._check_flags(server)
|
||||
|
||||
outembed = await self._list_flags(member)
|
||||
|
||||
if outembed:
|
||||
await ctx.send(embed=outembed)
|
||||
else:
|
||||
await ctx.send("This member has no flags!")
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command(aliases=['flagall'])
|
||||
async def allflag(self, ctx: commands.Context):
|
||||
"""Lists all flags for the server"""
|
||||
guild = ctx.guild
|
||||
await self._check_flags(guild)
|
||||
out = "All flags for {}\n".format(ctx.guild.name)
|
||||
|
||||
flags = await self.config.guild(guild).flags()
|
||||
flag_d = {}
|
||||
for memberid, flag_data in flags.items():
|
||||
if len(flag_data) > 0:
|
||||
member = guild.get_member(int(memberid))
|
||||
flag_d[member.display_name + member.discriminator] = len(flag_data)
|
||||
|
||||
for display_name, flag_count in sorted(flag_d.items()):
|
||||
out += "{} - **{}** flags".format(display_name, flag_count)
|
||||
|
||||
for page in pagify(out):
|
||||
await ctx.send(page)
|
||||
|
||||
async def _list_flags(self, member: discord.Member):
|
||||
"""Returns a pretty embed of flags on a member"""
|
||||
flags = await self.config.guild(member.guild).flags.get_raw(str(member.id), default=[])
|
||||
|
||||
embed = discord.Embed(title="Flags for " + member.display_name,
|
||||
description="User has {} active flags".format(len(flags)), color=0x804040)
|
||||
for flag in flags:
|
||||
embed.add_field(name="Reason: " + flag['reason'],
|
||||
value="Expires on " + str(date(flag['expireyear'], flag['expiremonth'], flag['expireday'])),
|
||||
inline=True)
|
||||
|
||||
embed.set_thumbnail(url=member.avatar_url)
|
||||
|
||||
return embed
|
||||
|
||||
async def _check_flags(self, guild: discord.Guild):
|
||||
"""Updates and removes expired flags"""
|
||||
flag_data = await self.config.guild(guild).flags()
|
||||
flag_d = {}
|
||||
for memberid, flags in flag_data.items():
|
||||
# for member in guild.members:
|
||||
# flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[])
|
||||
x = 0
|
||||
while x < len(flags):
|
||||
flag = flags[x]
|
||||
if date.today() >= date(flag['expireyear'], flag['expiremonth'], flag['expireday']):
|
||||
del flags[x]
|
||||
else:
|
||||
x += 1
|
||||
|
||||
await self.config.guild(guild).flags.set_raw(memberid, value=flags)
|
23
flag/info..json
Normal file
23
flag/info..json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Add expiring flags on members to track warnings or incidents",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing Flag! Get started with `[p]load flag` and `[p]help Flag`",
|
||||
"requirements": [],
|
||||
"short": "Add expiring flags to members",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"warning",
|
||||
"warn",
|
||||
"temp",
|
||||
"tools",
|
||||
"warning"
|
||||
]
|
||||
}
|
5
forcemention/__init__.py
Normal file
5
forcemention/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .forcemention import ForceMention
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(ForceMention(bot))
|
41
forcemention/forcemention.py
Normal file
41
forcemention/forcemention.py
Normal file
@ -0,0 +1,41 @@
|
||||
from discord.utils import get
|
||||
|
||||
from redbot.core import Config, checks, commands
|
||||
|
||||
from redbot.core.bot import Red
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class ForceMention(Cog):
|
||||
"""
|
||||
Mention the unmentionables
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_global = {}
|
||||
default_guild = {}
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@checks.admin_or_permissions(manage_roles=True)
|
||||
@commands.command()
|
||||
async def forcemention(self, ctx: commands.Context, role: str, *, message=''):
|
||||
"""
|
||||
Mentions that role, regardless if it's unmentionable
|
||||
"""
|
||||
role_obj = get(ctx.guild.roles, name=role)
|
||||
if role_obj is None:
|
||||
await ctx.maybe_send_embed("Couldn't find role named {}".format(role))
|
||||
return
|
||||
|
||||
if not role_obj.mentionable:
|
||||
await role_obj.edit(mentionable=True)
|
||||
await ctx.send("{}\n{}".format(role_obj.mention, message))
|
||||
await role_obj.edit(mentionable=False)
|
||||
else:
|
||||
await ctx.send("{}\n{}".format(role_obj.mention, message))
|
19
forcemention/info..json
Normal file
19
forcemention/info..json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Mentions roles that are unmentionable",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing ForceMention! Get started with `[p]load forcemention`, then `[p]forcemention`",
|
||||
"requirements": [],
|
||||
"short": "Mention unmentionables",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"utils"
|
||||
]
|
||||
}
|
@ -6,4 +6,4 @@ def setup(bot):
|
||||
n = Hangman(bot)
|
||||
data_manager.load_bundled_data(n, __file__)
|
||||
bot.add_cog(n)
|
||||
bot.add_listener(n._on_react, "on_reaction_add")
|
||||
bot.add_listener(n.on_react, "on_reaction_add")
|
||||
|
@ -2,12 +2,14 @@ from collections import defaultdict
|
||||
from random import randint
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from redbot.core import Config, checks
|
||||
from redbot.core.data_manager import cog_data_path, load_basic_configuration
|
||||
from redbot.core import Config, checks, commands
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class Hangman:
|
||||
class Hangman(Cog):
|
||||
"""Lets anyone play a game of hangman with custom phrases"""
|
||||
navigate = "🔼🔽"
|
||||
letters = "🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿"
|
||||
@ -17,6 +19,7 @@ class Hangman:
|
||||
self.config = Config.get_conf(self, identifier=1049711010310997110)
|
||||
default_guild = {
|
||||
"theface": ':thinking:',
|
||||
"emojis": True,
|
||||
}
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
@ -25,7 +28,7 @@ class Hangman:
|
||||
lambda: {"running": False, "hangman": 0, "guesses": [], "trackmessage": False, "answer": ''})
|
||||
self.path = str(cog_data_path(self)).replace('\\', '/')
|
||||
|
||||
self.answer_path = self.path+"/bundled_data/hanganswers.txt"
|
||||
self.answer_path = self.path + "/bundled_data/hanganswers.txt"
|
||||
|
||||
self.winbool = defaultdict(lambda: False)
|
||||
|
||||
@ -127,11 +130,12 @@ class Hangman:
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def hangset(self, ctx):
|
||||
"""Adjust hangman settings"""
|
||||
if not ctx.invoked_subcommand:
|
||||
await ctx.send_help()
|
||||
if ctx.invoked_subcommand is None:
|
||||
pass
|
||||
|
||||
@hangset.command(pass_context=True)
|
||||
async def face(self, ctx: commands.Context, theface):
|
||||
"""Set the face of the hangman"""
|
||||
message = ctx.message
|
||||
# Borrowing FlapJack's emoji validation
|
||||
# (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py)
|
||||
@ -149,6 +153,14 @@ class Hangman:
|
||||
await self._update_hanglist()
|
||||
await ctx.send("Face has been updated!")
|
||||
|
||||
@hangset.command(pass_context=True)
|
||||
async def toggleemoji(self, ctx: commands.Context):
|
||||
"""Toggles whether to automatically react with the alphabet"""
|
||||
|
||||
current = await self.config.guild(ctx.guild).emojis()
|
||||
await self.config.guild(ctx.guild).emojis.set(not current)
|
||||
await ctx.send("Emoji Letter reactions have been set to {}".format(not current))
|
||||
|
||||
@commands.command(aliases=['hang'], pass_context=True)
|
||||
async def hangman(self, ctx, guess: str = None):
|
||||
"""Play a game of hangman against the bot!"""
|
||||
@ -245,7 +257,7 @@ class Hangman:
|
||||
|
||||
await self._reprintgame(message)
|
||||
|
||||
async def _on_react(self, reaction, user):
|
||||
async def on_react(self, reaction, user):
|
||||
""" Thanks to flapjack reactpoll for guidelines
|
||||
https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py"""
|
||||
|
||||
@ -253,7 +265,7 @@ class Hangman:
|
||||
return
|
||||
|
||||
if user == self.bot.user:
|
||||
return # Don't remove bot's own reactions
|
||||
return # Don't react to bot's own reactions
|
||||
message = reaction.message
|
||||
emoji = reaction.emoji
|
||||
|
||||
@ -270,15 +282,27 @@ class Hangman:
|
||||
if str(emoji) == self.navigate[-1]:
|
||||
await self._reactmessage_nz(message)
|
||||
|
||||
async def _try_clear_reactions(self, message):
|
||||
try:
|
||||
await message.clear_reactions()
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
|
||||
async def _reactmessage_menu(self, message):
|
||||
"""React with menu options"""
|
||||
await message.clear_reactions()
|
||||
if not await self.config.guild(message.guild).emojis():
|
||||
return
|
||||
|
||||
await self._try_clear_reactions(message)
|
||||
|
||||
await message.add_reaction(self.navigate[0])
|
||||
await message.add_reaction(self.navigate[-1])
|
||||
|
||||
async def _reactmessage_am(self, message):
|
||||
await message.clear_reactions()
|
||||
if not await self.config.guild(message.guild).emojis():
|
||||
return
|
||||
|
||||
await self._try_clear_reactions(message)
|
||||
|
||||
for x in range(len(self.letters)):
|
||||
if x in [i for i, b in enumerate("ABCDEFGHIJKLM") if b not in self._guesslist(message.guild)]:
|
||||
@ -287,7 +311,10 @@ class Hangman:
|
||||
await message.add_reaction(self.navigate[-1])
|
||||
|
||||
async def _reactmessage_nz(self, message):
|
||||
await message.clear_reactions()
|
||||
if not await self.config.guild(message.guild).emojis():
|
||||
return
|
||||
|
||||
await self._try_clear_reactions(message)
|
||||
|
||||
for x in range(len(self.letters)):
|
||||
if x in [i for i, b in enumerate("NOPQRSTUVWXYZ") if b not in self._guesslist(message.guild)]:
|
||||
@ -297,11 +324,8 @@ class Hangman:
|
||||
|
||||
def _make_say(self, guild):
|
||||
c_say = "Guess this: " + str(self._hideanswer(guild)) + "\n"
|
||||
|
||||
c_say += "Used Letters: " + str(self._guesslist(guild)) + "\n"
|
||||
|
||||
c_say += self.hanglist[guild][self.the_data[guild]["hangman"]] + "\n"
|
||||
|
||||
c_say += self.navigate[0] + " for A-M, " + self.navigate[-1] + " for N-Z"
|
||||
|
||||
return c_say
|
||||
@ -330,9 +354,3 @@ class Hangman:
|
||||
|
||||
await self._reactmessage_menu(message)
|
||||
await self._checkdone(channel)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
n = Hangman(bot)
|
||||
bot.add_cog(n)
|
||||
bot.add_listener(n._on_react, "on_reaction_add")
|
||||
|
@ -9,7 +9,7 @@
|
||||
],
|
||||
"description": "Play Hangman with your friends",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing Hangman!",
|
||||
"install_msg": "Thank you for installing Hangman! Get started with `[p]load hangman`, then `[p]help Hangman`",
|
||||
"requirements": [],
|
||||
"short": "Play Hangman",
|
||||
"tags": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
import discord
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
from .utils.chat_formatting import pagify
|
||||
from .utils.chat_formatting import box
|
||||
@ -33,7 +33,7 @@ class Howdoi:
|
||||
"""Adjust howdoi settings
|
||||
Settings are reset on reload"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@howdoiset.command(pass_context=True, name="answers")
|
||||
async def howdoiset_answers(self, ctx, num_answers: int=1):
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"AUTHOR": "Bobloy",
|
||||
"INSTALL_MSG": "Thank you for installing Fox-Cogs by Bobloy",
|
||||
"NAME": "Fox-Cogs",
|
||||
"INSTALL_MSG": "Thank you for installing Fox-V3 by Bobloy",
|
||||
"NAME": "Fox-V3",
|
||||
"SHORT": "Cogs by Bobloy",
|
||||
"DESCRIPTION": "Cogs for RED Discord Bot by Bobloy"
|
||||
}
|
5
leaver/__init__.py
Normal file
5
leaver/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .leaver import Leaver
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Leaver(bot))
|
@ -1,9 +1,20 @@
|
||||
{
|
||||
"AUTHOR": "Bobloy",
|
||||
"INSTALL_MSG": "Thank you for installing leaver",
|
||||
"NAME": "Leaver",
|
||||
"SHORT": "Sends message on leave",
|
||||
"DESCRIPTION": "Keeps track of when people leave the server, and posts a message notifying",
|
||||
"TAGS": ["fox", "bobloy", "utilities", "tools", "tool"],
|
||||
"HIDDEN": false
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Keeps track of when people leave the server, and posts a message notifying",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing Leaver. Get started with `[p]load leaver`, then `[p]help Leaver`",
|
||||
"requirements": [],
|
||||
"short": "Send message on leave",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"utils",
|
||||
"tools"
|
||||
]
|
||||
}
|
@ -1,78 +1,45 @@
|
||||
import discord
|
||||
import os
|
||||
from datetime import datetime
|
||||
from discord.ext import commands
|
||||
|
||||
from .utils.dataIO import dataIO
|
||||
from .utils import checks
|
||||
from redbot.core import Config, checks, commands
|
||||
from redbot.core.commands import Context
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class Leaver:
|
||||
"""Creates a goodbye message when people leave"""
|
||||
class Leaver(Cog):
|
||||
"""
|
||||
Creates a goodbye message when people leave
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.path = "data/Fox-Cogs/leaver"
|
||||
self.file_path = "data/Fox-Cogs/leaver/leaver.json"
|
||||
self.the_data = dataIO.load_json(self.file_path)
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_guild = {
|
||||
"channel": ''
|
||||
}
|
||||
|
||||
def save_data(self):
|
||||
"""Saves the json"""
|
||||
dataIO.save_json(self.file_path, self.the_data)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.group(aliases=['setleaver'], pass_context=True, no_pm=True)
|
||||
@commands.group(aliases=['setleaver'])
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def leaverset(self, ctx):
|
||||
"""Adjust leaver settings"""
|
||||
|
||||
server = ctx.message.server
|
||||
if server.id not in self.the_data:
|
||||
self.the_data[server.id] = {}
|
||||
self.save_data()
|
||||
|
||||
|
||||
if ctx.invoked_subcommand is None:
|
||||
await self.bot.send_cmd_help(ctx)
|
||||
pass
|
||||
|
||||
@leaverset.command(pass_context=True, no_pm=True)
|
||||
async def channel(self, ctx):
|
||||
server = ctx.message.server
|
||||
if 'CHANNEL' not in self.the_data[server.id]:
|
||||
self.the_data[server.id]['CHANNEL'] = ''
|
||||
|
||||
@leaverset.command()
|
||||
async def channel(self, ctx: Context):
|
||||
guild = ctx.guild
|
||||
await self.config.guild(guild).channel.set(ctx.channel.id)
|
||||
await ctx.send("Channel set to " + ctx.channel.name)
|
||||
|
||||
self.the_data[server.id]['CHANNEL'] = ctx.message.channel.id
|
||||
self.save_data()
|
||||
await self.bot.say("Channel set to "+ctx.message.channel.name)
|
||||
async def on_member_remove(self, member: discord.Member):
|
||||
guild = member.guild
|
||||
channel = await self.config.guild(guild).channel()
|
||||
|
||||
async def when_leave(self, member):
|
||||
server = member.server
|
||||
if server.id in self.the_data:
|
||||
await self.bot.send_message(server.get_channel(self.the_data[server.id]['CHANNEL']),
|
||||
str(member) + "(*" + str(member.nick) +"*) has left the server!")
|
||||
if channel != '':
|
||||
channel = guild.get_channel(channel)
|
||||
await channel.send(str(member) + "(*" + str(member.nick) + "*) has left the server!")
|
||||
else:
|
||||
await self.bot.send_message(server.default_channel.id, str(member) + " (*" + str(member.nick) +"*) has left the server!")
|
||||
|
||||
|
||||
def check_folders():
|
||||
if not os.path.exists("data/Fox-Cogs"):
|
||||
print("Creating data/Fox-Cogs folder...")
|
||||
os.makedirs("data/Fox-Cogs")
|
||||
|
||||
if not os.path.exists("data/Fox-Cogs/leaver"):
|
||||
print("Creating data/Fox-Cogs/leaver folder...")
|
||||
os.makedirs("data/Fox-Cogs/leaver")
|
||||
|
||||
|
||||
def check_files():
|
||||
if not dataIO.is_valid_json("data/Fox-Cogs/leaver/leaver.json"):
|
||||
dataIO.save_json("data/Fox-Cogs/leaver/leaver.json", {})
|
||||
|
||||
|
||||
def setup(bot):
|
||||
check_folders()
|
||||
check_files()
|
||||
q = Leaver(bot)
|
||||
bot.add_listener(q.when_leave, "on_member_remove")
|
||||
bot.add_cog(q)
|
||||
|
||||
pass
|
||||
|
5
lovecalculator/__init__.py
Normal file
5
lovecalculator/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .lovecalculator import LoveCalculator
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(LoveCalculator(bot))
|
23
lovecalculator/info.json
Normal file
23
lovecalculator/info.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy",
|
||||
"SnappyDragon"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Calculate the love percentage for two users",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing LoveCalculator. Love is in the air.\n Get started with `[p]load lovecalculator`, then `[p]help LoveCalculator`",
|
||||
"requirements": [
|
||||
"beautifulsoup4"
|
||||
],
|
||||
"short": "Calculate love percentage",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"fun",
|
||||
"love"
|
||||
]
|
||||
}
|
47
lovecalculator/lovecalculator.py
Normal file
47
lovecalculator/lovecalculator.py
Normal file
@ -0,0 +1,47 @@
|
||||
import aiohttp
|
||||
import discord
|
||||
from bs4 import BeautifulSoup
|
||||
from redbot.core import commands
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class LoveCalculator(Cog):
|
||||
"""Calculate the love percentage for two users!"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(aliases=['lovecalc'])
|
||||
async def lovecalculator(self, ctx: commands.Context, lover: discord.Member, loved: discord.Member):
|
||||
"""Calculate the love percentage!"""
|
||||
|
||||
x = lover.display_name
|
||||
y = loved.display_name
|
||||
|
||||
url = 'https://www.lovecalculator.com/love.php?name1={}&name2={}'.format(x.replace(" ", "+"),
|
||||
y.replace(" ", "+"))
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as response:
|
||||
soup_object = BeautifulSoup(await response.text(), "html.parser")
|
||||
try:
|
||||
description = soup_object.find('div', attrs={'class': 'result score'}).get_text().strip()
|
||||
except:
|
||||
description = 'Dr. Love is busy right now'
|
||||
|
||||
try:
|
||||
z = description[:2]
|
||||
z = int(z)
|
||||
if z > 50:
|
||||
emoji = '❤'
|
||||
else:
|
||||
emoji = '💔'
|
||||
title = 'Dr. Love says that the love percentage for {} and {} is:'.format(x, y)
|
||||
except:
|
||||
emoji = ''
|
||||
title = 'Dr. Love has left a note for you.'
|
||||
|
||||
description = emoji + ' ' + description + ' ' + emoji
|
||||
em = discord.Embed(title=title, description=description, color=discord.Color.red())
|
||||
await ctx.send(embed=em)
|
20
lseen/info..json
Normal file
20
lseen/info..json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Keep track of when users were last seen online",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing LastSeen. Get started with `[p]load lseen`, then `[p]help LastSeen`",
|
||||
"requirements": ["python-dateutil"],
|
||||
"short": "Last seen tracker",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"utils",
|
||||
"tools"
|
||||
]
|
||||
}
|
@ -2,14 +2,18 @@ from datetime import datetime
|
||||
|
||||
import dateutil.parser
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from redbot.core import Config, RedContext
|
||||
|
||||
from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core import commands
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class LastSeen:
|
||||
class LastSeen(Cog):
|
||||
"""
|
||||
V3 Cog Template
|
||||
Report when a user was last seen online
|
||||
"""
|
||||
|
||||
online_status = discord.Status.online
|
||||
@ -37,13 +41,13 @@ class LastSeen:
|
||||
return d
|
||||
|
||||
@commands.group(aliases=['setlseen'], name='lseenset')
|
||||
async def lset(self, ctx: RedContext):
|
||||
async def lset(self, ctx: commands.Context):
|
||||
"""Change settings for lseen"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@lset.command(name="toggle")
|
||||
async def lset_toggle(self, ctx: RedContext):
|
||||
async def lset_toggle(self, ctx: commands.Context):
|
||||
"""Toggles tracking seen for this server"""
|
||||
enabled = not await self.config.guild(ctx.guild).enabled()
|
||||
await self.config.guild(ctx.guild).enabled.set(
|
||||
@ -54,11 +58,9 @@ class LastSeen:
|
||||
"Enabled" if enabled else "Disabled"))
|
||||
|
||||
@commands.command(aliases=['lastseen'])
|
||||
async def lseen(self, ctx: RedContext, member: discord.Member):
|
||||
async def lseen(self, ctx: commands.Context, member: discord.Member):
|
||||
"""
|
||||
Just says the time the user was last seen
|
||||
|
||||
:param member:
|
||||
"""
|
||||
|
||||
if member.status != self.offline_status:
|
||||
@ -72,19 +74,11 @@ class LastSeen:
|
||||
|
||||
# embed = discord.Embed(
|
||||
# description="{} was last seen at this date and time".format(member.display_name),
|
||||
# timestamp=self.get_date_time(last_seen))
|
||||
# timestamp=last_seen)
|
||||
|
||||
embed = discord.Embed(timestamp=last_seen)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
# async def on_socket_raw_receive(self, data):
|
||||
# try:
|
||||
# if type(data) == str:
|
||||
# raw = json.loads(data)
|
||||
# print(data)
|
||||
# except:
|
||||
# print(data)
|
||||
|
||||
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
||||
if before.status != self.offline_status and after.status == self.offline_status:
|
||||
if not await self.config.guild(before.guild).enabled():
|
||||
|
5
nudity/__init__.py
Normal file
5
nudity/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .nudity import Nudity
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Nudity(bot))
|
20
nudity/info..json
Normal file
20
nudity/info..json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Keep track of when users were last seen online",
|
||||
"hidden": true,
|
||||
"install_msg": "Thank you for installing LastSeen. Get started with `[p]load nudity`, then `[p]help Nudity`",
|
||||
"requirements": ["nudepy"],
|
||||
"short": "Last seen tracker",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"utils",
|
||||
"tools"
|
||||
]
|
||||
}
|
45
nudity/nudity.py
Normal file
45
nudity/nudity.py
Normal file
@ -0,0 +1,45 @@
|
||||
from datetime import datetime
|
||||
|
||||
import discord
|
||||
import nude
|
||||
from nude import Nude
|
||||
from redbot.core import Config
|
||||
from redbot.core import commands
|
||||
from redbot.core.bot import Red
|
||||
|
||||
|
||||
class Nudity:
|
||||
"""
|
||||
V3 Cog Template
|
||||
"""
|
||||
|
||||
online_status = discord.Status.online
|
||||
|
||||
offline_status = discord.Status.offline
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
|
||||
default_guild = {
|
||||
"enabled": False
|
||||
}
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.command(aliases=['togglenudity'], name='nudity')
|
||||
async def nudity(self, ctx: commands.Context):
|
||||
"""Toggle nude-checking on or off"""
|
||||
is_on = await self.config.guild(ctx.guild).enabled()
|
||||
await self.config.guild(ctx.guild).enabled.set(not is_on)
|
||||
await ctx.send("Nude checking is now set to {}".format(not is_on))
|
||||
|
||||
async def on_message(self, message: discord.Message):
|
||||
is_on = await self.config.guild(message.guild).enabled()
|
||||
if not is_on:
|
||||
return
|
||||
|
||||
if not message.attachments:
|
||||
return
|
||||
|
||||
|
5
planttycoon/__init__.py
Normal file
5
planttycoon/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .planttycoon import PlantTycoon
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(PlantTycoon(bot))
|
11
planttycoon/data/badges.json
Normal file
11
planttycoon/data/badges.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"badges": {
|
||||
"Flower Power": {},
|
||||
"Fruit Brute": {},
|
||||
"Sporadic": {},
|
||||
"Odd-pod": {},
|
||||
"Greenfingers": {},
|
||||
"Nobel Peas Prize": {},
|
||||
"Annualsary": {}
|
||||
}
|
||||
}
|
22
planttycoon/data/defaults.json
Normal file
22
planttycoon/data/defaults.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"points": {
|
||||
"buy": 5,
|
||||
"add_health": 5,
|
||||
"fertilize": 10,
|
||||
"pruning": 20,
|
||||
"pesticide": 25,
|
||||
"growing": 5,
|
||||
"damage": 25
|
||||
},
|
||||
"timers": {
|
||||
"degradation": 1,
|
||||
"completion": 1,
|
||||
"notification": 5
|
||||
},
|
||||
"degradation": {
|
||||
"base_degradation": 1.5
|
||||
},
|
||||
"notification": {
|
||||
"max_health": 50
|
||||
}
|
||||
}
|
7
planttycoon/data/notifications.json
Normal file
7
planttycoon/data/notifications.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"messages": [
|
||||
"The soil seems dry, maybe you could give your plant some water?",
|
||||
"Your plant seems a bit droopy. I would give it some fertilizer if I were you.",
|
||||
"Your plant seems a bit too overgrown. You should probably trim it a bit."
|
||||
]
|
||||
}
|
690
planttycoon/data/plants.json
Normal file
690
planttycoon/data/plants.json
Normal file
@ -0,0 +1,690 @@
|
||||
{
|
||||
"plants": [
|
||||
{
|
||||
"name": "Poppy",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/S4hjyUX.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Dandelion",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/emqnQP2.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Daisy",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/lcFq4AB.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Chrysanthemum",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/5jLtqWL.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Pansy",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/f7TgD1b.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Lavender",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/g3OmOSK.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Lily",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/0hzy7lO.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Petunia",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/rJm8ISv.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Sunflower",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/AzgzQK9.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Daffodil",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/pnCCRsH.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Clover",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/jNTgirw.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Tulip",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/kodIFjE.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Rose",
|
||||
"article": "a",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/sdTNiOH.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Aster",
|
||||
"article": "an",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/1tN04Hl.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Aloe Vera",
|
||||
"article": "an",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/WFAYIpx.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Orchid",
|
||||
"article": "an",
|
||||
"time": 3600,
|
||||
"rarity": "common",
|
||||
"image": "http://i.imgur.com/IQrQYDC.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.625,
|
||||
"threshold": 110,
|
||||
"badge": "Flower Power",
|
||||
"reward": 600
|
||||
},
|
||||
{
|
||||
"name": "Dragon Fruit Plant",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/pfngpDS.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Mango Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/ybR78Oc.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Lychee Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/w9LkfhX.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Durian Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/jh249fz.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Fig Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/YkhnpEV.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Jack Fruit Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/2D79TlA.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Prickly Pear Plant",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/GrcGAGj.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Pineapple Plant",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/VopYQtr.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Citron Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/zh7Dr23.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Cherimoya Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/H62gQK6.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Mangosteen Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/McNnMqa.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Guava Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/iy8WgPt.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Orange Tree",
|
||||
"article": "an",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/lwjEJTm.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Apple Tree",
|
||||
"article": "an",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/QI3UTR3.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Sapodilla Tree",
|
||||
"article": "a",
|
||||
"time": 5400,
|
||||
"rarity": "uncommon",
|
||||
"image": "http://i.imgur.com/6BvO5Fu.jpg",
|
||||
"health": 100,
|
||||
"degradation": 0.75,
|
||||
"threshold": 110,
|
||||
"badge": "Fruit Brute",
|
||||
"reward": 1200
|
||||
},
|
||||
{
|
||||
"name": "Franklin Tree",
|
||||
"article": "a",
|
||||
"time": 7200,
|
||||
"rarity": "rare",
|
||||
"image": "http://i.imgur.com/hoh17hp.jpg",
|
||||
"health": 100,
|
||||
"degradation": 1,
|
||||
"threshold": 110,
|
||||
"badge": "Sporadic",
|
||||
"reward": 2400
|
||||
},
|
||||
{
|
||||
"name": "Parrot's Beak",
|
||||
"article": "a",
|
||||
"time": 7200,
|
||||
"rarity": "rare",
|
||||
"image": "http://i.imgur.com/lhSjfQY.jpg",
|
||||
"health": 100,
|
||||
"degradation": 1,
|
||||
"threshold": 110,
|
||||
"badge": "Sporadic",
|
||||
"reward": 2400
|
||||
},
|
||||
{
|
||||
"name": "Koki'o",
|
||||
"article": "a",
|
||||
"time": 7200,
|
||||
"rarity": "rare",
|
||||
"image": "http://i.imgur.com/Dhw9ync.jpg",
|
||||
"health": 100,
|
||||
"degradation": 1,
|
||||
"threshold": 110,
|
||||
"badge": "Sporadic",
|
||||
"reward": 2400
|
||||
},
|
||||
{
|
||||
"name": "Jade Vine",
|
||||
"article": "a",
|
||||
"time": 7200,
|
||||
"rarity": "rare",
|
||||
"image": "http://i.imgur.com/h4fJo2R.jpg",
|
||||
"health": 100,
|
||||
"degradation": 1,
|
||||
"threshold": 110,
|
||||
"badge": "Sporadic",
|
||||
"reward": 2400
|
||||
},
|
||||
{
|
||||
"name": "Venus Fly Trap",
|
||||
"article": "a",
|
||||
"time": 7200,
|
||||
"rarity": "rare",
|
||||
"image": "http://i.imgur.com/NoSdxXh.jpg",
|
||||
"health": 100,
|
||||
"degradation": 1,
|
||||
"threshold": 110,
|
||||
"badge": "Sporadic",
|
||||
"reward": 2400
|
||||
},
|
||||
{
|
||||
"name": "Chocolate Cosmos",
|
||||
"article": "a",
|
||||
"time": 7200,
|
||||
"rarity": "rare",
|
||||
"image": "http://i.imgur.com/4ArSekX.jpg",
|
||||
"health": 100,
|
||||
"degradation": 1,
|
||||
"threshold": 110,
|
||||
"badge": "Sporadic",
|
||||
"reward": 2400
|
||||
},
|
||||
{
|
||||
"name": "Pizza Plant",
|
||||
"article": "a",
|
||||
"time": 9000,
|
||||
"rarity": "super-rare",
|
||||
"image": "http://i.imgur.com/ASZXr7C.png",
|
||||
"health": 100,
|
||||
"degradation": 1,
|
||||
"threshold": 110,
|
||||
"badge": "Odd-pod",
|
||||
"reward": 3600
|
||||
},
|
||||
{
|
||||
"name": "tba",
|
||||
"article": "a",
|
||||
"time": 9000,
|
||||
"rarity": "super-rare",
|
||||
"image": "tba",
|
||||
"health": 100,
|
||||
"degradation": 1.5,
|
||||
"threshold": 110,
|
||||
"badge": "Odd-pod",
|
||||
"reward": 3600
|
||||
},
|
||||
{
|
||||
"name": "Pirahna Plant",
|
||||
"article": "a",
|
||||
"time": 9000,
|
||||
"rarity": "super-rare",
|
||||
"image": "http://i.imgur.com/c03i9W7.jpg",
|
||||
"health": 100,
|
||||
"degradation": 1.5,
|
||||
"threshold": 110,
|
||||
"badge": "Odd-pod",
|
||||
"reward": 3600
|
||||
},
|
||||
{
|
||||
"name": "tba",
|
||||
"article": "a",
|
||||
"time": 9000,
|
||||
"rarity": "super-rare",
|
||||
"image": "tba",
|
||||
"health": 100,
|
||||
"degradation": 1.5,
|
||||
"threshold": 110,
|
||||
"badge": "Odd-pod",
|
||||
"reward": 3600
|
||||
},
|
||||
{
|
||||
"name": "Peashooter",
|
||||
"article": "a",
|
||||
"time": 9000,
|
||||
"rarity": "super-rare",
|
||||
"image": "https://i.imgur.com/Vo4v2Ry.png",
|
||||
"health": 100,
|
||||
"degradation": 1.5,
|
||||
"threshold": 110,
|
||||
"badge": "Odd-pod",
|
||||
"reward": 3600
|
||||
},
|
||||
{
|
||||
"name": "tba",
|
||||
"article": "a",
|
||||
"time": 10800,
|
||||
"rarity": "epic",
|
||||
"image": "tba",
|
||||
"health": 100,
|
||||
"degradation": 2,
|
||||
"threshold": 110,
|
||||
"badge": "Greenfingers",
|
||||
"reward": 5400
|
||||
},
|
||||
{
|
||||
"name": "Pikmin",
|
||||
"article": "a",
|
||||
"time": 10800,
|
||||
"rarity": "epic",
|
||||
"image": "http://i.imgur.com/sizf7hE.png",
|
||||
"health": 100,
|
||||
"degradation": 2,
|
||||
"threshold": 110,
|
||||
"badge": "Greenfingers",
|
||||
"reward": 5400
|
||||
},
|
||||
{
|
||||
"name": "Flora Colossus",
|
||||
"article": "a",
|
||||
"time": 10800,
|
||||
"rarity": "epic",
|
||||
"image": "http://i.imgur.com/9f5QzaW.jpg",
|
||||
"health": 100,
|
||||
"degradation": 2,
|
||||
"threshold": 110,
|
||||
"badge": "Greenfingers",
|
||||
"reward": 5400
|
||||
},
|
||||
{
|
||||
"name": "Plantera Bulb",
|
||||
"article": "a",
|
||||
"time": 10800,
|
||||
"rarity": "epic",
|
||||
"image": "https://i.imgur.com/ExqLLHO.png",
|
||||
"health": 100,
|
||||
"degradation": 2,
|
||||
"threshold": 110,
|
||||
"badge": "Greenfingers",
|
||||
"reward": 5400
|
||||
},
|
||||
{
|
||||
"name": "Chorus Tree",
|
||||
"article": "an",
|
||||
"time": 10800,
|
||||
"rarity": "epic",
|
||||
"image": "https://i.imgur.com/tv2B72j.png",
|
||||
"health": 100,
|
||||
"degradation": 2,
|
||||
"threshold": 110,
|
||||
"badge": "Greenfingers",
|
||||
"reward": 5400
|
||||
},
|
||||
{
|
||||
"name": "Money Tree",
|
||||
"article": "a",
|
||||
"time": 35400,
|
||||
"rarity": "legendary",
|
||||
"image": "http://i.imgur.com/MIJQDLL.jpg",
|
||||
"health": 100,
|
||||
"degradation": 3,
|
||||
"threshold": 110,
|
||||
"badge": "Nobel Peas Prize",
|
||||
"reward": 10800
|
||||
},
|
||||
{
|
||||
"name": "Truffula Tree",
|
||||
"article": "a",
|
||||
"time": 35400,
|
||||
"rarity": "legendary",
|
||||
"image": "http://i.imgur.com/cFSmaHH.png",
|
||||
"health": 100,
|
||||
"degradation": 3,
|
||||
"threshold": 110,
|
||||
"badge": "Nobel Peas Prize",
|
||||
"reward": 10800
|
||||
},
|
||||
{
|
||||
"name": "Whomping Willow",
|
||||
"article": "a",
|
||||
"time": 35400,
|
||||
"rarity": "legendary",
|
||||
"image": "http://i.imgur.com/Ibwm2xY.jpg",
|
||||
"health": 100,
|
||||
"degradation": 3,
|
||||
"threshold": 110,
|
||||
"badge": "Nobel Peas Prize",
|
||||
"reward": 10800
|
||||
}
|
||||
],
|
||||
"event": {
|
||||
"January": {
|
||||
"name": "Tanabata Tree",
|
||||
"article": "a",
|
||||
"time": 70800,
|
||||
"rarity": "event",
|
||||
"image": "http://i.imgur.com/FD38JJj.jpg",
|
||||
"health": 100,
|
||||
"degradation": 9,
|
||||
"threshold": 110,
|
||||
"badge": "Annualsary",
|
||||
"reward": 21600
|
||||
},
|
||||
"February": {
|
||||
"name": "Chocolate Rose",
|
||||
"article": "a",
|
||||
"time": 70800,
|
||||
"rarity": "event",
|
||||
"image": "http://i.imgur.com/Sqg6pcG.jpg",
|
||||
"health": 100,
|
||||
"degradation": 9,
|
||||
"threshold": 110,
|
||||
"badge": "Annualsary",
|
||||
"reward": 21600
|
||||
},
|
||||
"March": {
|
||||
"name": "Shamrock",
|
||||
"article": "a",
|
||||
"time": 70800,
|
||||
"rarity": "event",
|
||||
"image": "http://i.imgur.com/kVig04M.jpg",
|
||||
"health": 100,
|
||||
"degradation": 9,
|
||||
"threshold": 110,
|
||||
"badge": "Annualsary",
|
||||
"reward": 21600
|
||||
},
|
||||
"April": {
|
||||
"name": "Easter Egg Eggplant",
|
||||
"article": "an",
|
||||
"time": 70800,
|
||||
"rarity": "event",
|
||||
"image": "http://i.imgur.com/5jltGQa.jpg",
|
||||
"health": 100,
|
||||
"degradation": 9,
|
||||
"threshold": 110,
|
||||
"badge": "Annualsary",
|
||||
"reward": 21600
|
||||
},
|
||||
"October": {
|
||||
"name": "Jack O' Lantern",
|
||||
"article": "a",
|
||||
"time": 70800,
|
||||
"rarity": "event",
|
||||
"image": "http://i.imgur.com/efApsxG.jpg",
|
||||
"health": 100,
|
||||
"degradation": 9,
|
||||
"threshold": 110,
|
||||
"badge": "Annualsary",
|
||||
"reward": 21600
|
||||
},
|
||||
"November": {
|
||||
"name": "Mayflower",
|
||||
"article": "a",
|
||||
"time": 70800,
|
||||
"rarity": "event",
|
||||
"image": "http://i.imgur.com/nntNtoL.jpg",
|
||||
"health": 100,
|
||||
"degradation": 9,
|
||||
"threshold": 110,
|
||||
"badge": "Annualsary",
|
||||
"reward": 21600
|
||||
},
|
||||
"December": {
|
||||
"name": "Holly",
|
||||
"article": "a",
|
||||
"time": 70800,
|
||||
"rarity": "event",
|
||||
"image": "http://i.imgur.com/maDLmJC.jpg",
|
||||
"health": 100,
|
||||
"degradation": 9,
|
||||
"threshold": 110,
|
||||
"badge": "Annualsary",
|
||||
"reward": 21600
|
||||
}
|
||||
}
|
||||
}
|
42
planttycoon/data/products.json
Normal file
42
planttycoon/data/products.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"water": {
|
||||
"cost": 5,
|
||||
"health": 10,
|
||||
"damage": 45,
|
||||
"modifier": 0,
|
||||
"category": "water",
|
||||
"uses": 1
|
||||
},
|
||||
"manure": {
|
||||
"cost": 20,
|
||||
"health": 20,
|
||||
"damage": 55,
|
||||
"modifier": -0.035,
|
||||
"category": "fertilizer",
|
||||
"uses": 1
|
||||
},
|
||||
"vermicompost": {
|
||||
"cost": 35,
|
||||
"health": 30,
|
||||
"damage": 60,
|
||||
"modifier": -0.5,
|
||||
"category": "fertilizer",
|
||||
"uses": 1
|
||||
},
|
||||
"nitrates": {
|
||||
"cost": 70,
|
||||
"health": 60,
|
||||
"damage": 75,
|
||||
"modifier": -0.08,
|
||||
"category": "fertilizer",
|
||||
"uses": 1
|
||||
},
|
||||
"pruner": {
|
||||
"cost": 500,
|
||||
"health": 40,
|
||||
"damage": 90,
|
||||
"modifier": -0.065,
|
||||
"category": "tool",
|
||||
"uses": 10
|
||||
}
|
||||
}
|
22
planttycoon/info.json
Normal file
22
planttycoon/info.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy",
|
||||
"SnappyDragon",
|
||||
"PaddoInWonderland"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Grow your own plants! Be sure to take care of it. Do `[p]gardening` to get started",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing PlantTycoon. Check out all the commands with `[p]help PlantTycoon`",
|
||||
"requirements": [],
|
||||
"short": "Grow your own plants! Do `[p]gardening` to get started.",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"games",
|
||||
"environment"
|
||||
]
|
||||
}
|
1378
planttycoon/planttycoon.py
Normal file
1378
planttycoon/planttycoon.py
Normal file
File diff suppressed because it is too large
Load Diff
5
qrinvite/__init__.py
Normal file
5
qrinvite/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .qrinvite import QRInvite
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(QRInvite(bot))
|
23
qrinvite/info..json
Normal file
23
qrinvite/info..json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Create a QR code invite for the server",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing QRInvite! Get started with `[p]load qrinvite`, then `[p]help QRInvite`",
|
||||
"requirements": [
|
||||
"MyQR"
|
||||
],
|
||||
"short": "Create a QR code invite",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"tools",
|
||||
"qr",
|
||||
"code"
|
||||
]
|
||||
}
|
87
qrinvite/qrinvite.py
Normal file
87
qrinvite/qrinvite.py
Normal file
@ -0,0 +1,87 @@
|
||||
import pathlib
|
||||
|
||||
import aiohttp
|
||||
import discord
|
||||
from MyQR import myqr
|
||||
from PIL import Image
|
||||
from redbot.core import Config, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class QRInvite(Cog):
|
||||
"""
|
||||
V3 Cog Template
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_global = {}
|
||||
default_guild = {}
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.command()
|
||||
async def qrinvite(self, ctx: commands.Context, invite: str = None, colorized: bool = False, image_url: str = None):
|
||||
"""
|
||||
Create a custom QR code invite for this server
|
||||
"""
|
||||
if invite is None:
|
||||
try:
|
||||
invite = await ctx.channel.create_invite()
|
||||
except discord.Forbidden:
|
||||
try:
|
||||
invite = await ctx.channel.invites()
|
||||
invite = invite[0]
|
||||
except discord.Forbidden:
|
||||
await ctx.send("No permission to get an invite, please provide one")
|
||||
return
|
||||
invite = invite.code
|
||||
|
||||
if image_url is None:
|
||||
image_url = ctx.guild.icon_url
|
||||
|
||||
if image_url == "": # Still
|
||||
await ctx.send(
|
||||
"Could not get an image, please provide one. *(`{}help qrinvite` for details)*".format(ctx.prefix))
|
||||
return
|
||||
|
||||
extension = pathlib.Path(image_url).parts[-1].replace('.', '?').split('?')[1]
|
||||
|
||||
path: pathlib.Path = cog_data_path(self)
|
||||
image_path = path / (ctx.guild.icon + "." + extension)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(image_url) as response:
|
||||
image = await response.read()
|
||||
|
||||
with image_path.open("wb") as file:
|
||||
file.write(image)
|
||||
|
||||
if extension == "webp":
|
||||
new_path = convert_png(str(image_path))
|
||||
else:
|
||||
new_path = str(image_path)
|
||||
|
||||
myqr.run(invite, picture=new_path, save_name=ctx.guild.icon + "_qrcode.png",
|
||||
save_dir=str(cog_data_path(self)), colorized=colorized, )
|
||||
|
||||
png_path: pathlib.Path = path / (ctx.guild.icon + "_qrcode.png")
|
||||
with png_path.open("rb") as png_fp:
|
||||
await ctx.send(file=discord.File(png_fp.read(), "qrcode.png"))
|
||||
|
||||
|
||||
def convert_png(path):
|
||||
im = Image.open(path)
|
||||
im.load()
|
||||
alpha = im.split()[-1]
|
||||
im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255)
|
||||
mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
|
||||
im.paste(255, mask)
|
||||
new_path = path.replace(".webp", ".png")
|
||||
im.save(new_path, transparency=255)
|
||||
return new_path
|
@ -2,7 +2,7 @@
|
||||
"author" : ["Bobloy"],
|
||||
"bot_version" : [3,0,0],
|
||||
"description" : "Cog to prevent reactions on specific messages from certain users",
|
||||
"hidden" : false,
|
||||
"hidden" : true,
|
||||
"install_msg" : "Thank you for installing ReactRestrict.",
|
||||
"requirements" : [],
|
||||
"short" : "[Incomplete] Prevent reactions",
|
||||
|
@ -1,11 +1,12 @@
|
||||
import asyncio
|
||||
from typing import List, Union
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
from redbot.core import Config
|
||||
from redbot.core import commands
|
||||
from redbot.core.bot import Red
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class ReactRestrictCombo:
|
||||
@ -15,8 +16,8 @@ class ReactRestrictCombo:
|
||||
|
||||
def __eq__(self, other: "ReactRestrictCombo"):
|
||||
return (
|
||||
self.message_id == other.message_id and
|
||||
self.role_id == other.role_id
|
||||
self.message_id == other.message_id and
|
||||
self.role_id == other.role_id
|
||||
)
|
||||
|
||||
def to_json(self):
|
||||
@ -33,7 +34,7 @@ class ReactRestrictCombo:
|
||||
)
|
||||
|
||||
|
||||
class ReactRestrict:
|
||||
class ReactRestrict(Cog):
|
||||
"""
|
||||
Prevent specific roles from reacting to specific messages
|
||||
"""
|
||||
@ -79,14 +80,10 @@ class ReactRestrict:
|
||||
async def add_reactrestrict(self, message_id: int, role: discord.Role):
|
||||
"""
|
||||
Adds a react|role combo.
|
||||
|
||||
:param int message_id:
|
||||
:param str or int emoji:
|
||||
:param discord.Role role:
|
||||
"""
|
||||
# is_custom = True
|
||||
# if isinstance(emoji, str):
|
||||
# is_custom = False
|
||||
# is_custom = False
|
||||
|
||||
combo = ReactRestrictCombo(message_id, role.id)
|
||||
|
||||
@ -98,10 +95,10 @@ class ReactRestrict:
|
||||
|
||||
async def remove_react(self, message_id: int, role: discord.Role):
|
||||
"""
|
||||
Removes a given reaction.
|
||||
Removes a given reaction
|
||||
|
||||
:param int message_id:
|
||||
:param str or int emoji:
|
||||
:param message_id:
|
||||
:param role:
|
||||
:return:
|
||||
"""
|
||||
current_combos = await self.combo_list()
|
||||
@ -112,14 +109,13 @@ class ReactRestrict:
|
||||
if to_keep != current_combos:
|
||||
await self.set_combo_list(to_keep)
|
||||
|
||||
async def has_reactrestrict_combo(self, message_id: int)\
|
||||
async def has_reactrestrict_combo(self, message_id: int) \
|
||||
-> (bool, List[ReactRestrictCombo]):
|
||||
"""
|
||||
Determines if there is an existing role combo for a given message
|
||||
Determines if there is an existing role combo for a given message
|
||||
and emoji ID.
|
||||
|
||||
:param int message_id:
|
||||
:param str or int emoji:
|
||||
:param message_id:
|
||||
:return:
|
||||
"""
|
||||
if not await self.is_registered(message_id):
|
||||
@ -172,27 +168,23 @@ class ReactRestrict:
|
||||
raise LookupError("No role found.")
|
||||
|
||||
return role
|
||||
|
||||
async def _get_message_from_channel(self, channel_id: int, message_id: int)\
|
||||
|
||||
async def _get_message_from_channel(self, channel_id: int, message_id: int) \
|
||||
-> Union[discord.Message, None]:
|
||||
"""
|
||||
Tries to find a message by ID in the current guild context.
|
||||
|
||||
:param ctx:
|
||||
:param message_id:
|
||||
:return:
|
||||
"""
|
||||
channel = self.bot.get_channel(channel_id)
|
||||
try:
|
||||
return await channel.get_message(message_id)
|
||||
except discord.NotFound:
|
||||
pass
|
||||
except AttributeError: # VoiceChannel object has no attribute 'get_message'
|
||||
except AttributeError: # VoiceChannel object has no attribute 'get_message'
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
async def _get_message(self, ctx: commands.Context, message_id: int)\
|
||||
|
||||
async def _get_message(self, ctx: commands.Context, message_id: int) \
|
||||
-> Union[discord.Message, None]:
|
||||
"""
|
||||
Tries to find a message by ID in the current guild context.
|
||||
@ -206,12 +198,10 @@ class ReactRestrict:
|
||||
return await channel.get_message(message_id)
|
||||
except discord.NotFound:
|
||||
pass
|
||||
except AttributeError: # VoiceChannel object has no attribute 'get_message'
|
||||
except AttributeError: # VoiceChannel object has no attribute 'get_message'
|
||||
pass
|
||||
except discord.Forbidden: # No access to channel, skip
|
||||
pass
|
||||
|
||||
|
||||
|
||||
return None
|
||||
|
||||
@ -221,7 +211,7 @@ class ReactRestrict:
|
||||
Base command for this cog. Check help for the commands list.
|
||||
"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@reactrestrict.command()
|
||||
async def add(self, ctx: commands.Context, message_id: int, *, role: discord.Role):
|
||||
@ -235,18 +225,18 @@ class ReactRestrict:
|
||||
return
|
||||
|
||||
# try:
|
||||
# emoji, actual_emoji = await self._wait_for_emoji(ctx)
|
||||
# emoji, actual_emoji = await self._wait_for_emoji(ctx)
|
||||
# except asyncio.TimeoutError:
|
||||
# await ctx.send("You didn't respond in time, please redo this command.")
|
||||
# return
|
||||
|
||||
# await ctx.send("You didn't respond in time, please redo this command.")
|
||||
# return
|
||||
#
|
||||
# try:
|
||||
# await message.add_reaction(actual_emoji)
|
||||
# await message.add_reaction(actual_emoji)
|
||||
# except discord.HTTPException:
|
||||
# await ctx.send("I can't add that emoji because I'm not in the guild that"
|
||||
# " owns it.")
|
||||
# return
|
||||
|
||||
# await ctx.send("I can't add that emoji because I'm not in the guild that"
|
||||
# " owns it.")
|
||||
# return
|
||||
#
|
||||
# noinspection PyTypeChecker
|
||||
await self.add_reactrestrict(message_id, role)
|
||||
|
||||
@ -258,10 +248,10 @@ class ReactRestrict:
|
||||
Removes role associated with a given reaction.
|
||||
"""
|
||||
# try:
|
||||
# emoji, actual_emoji = await self._wait_for_emoji(ctx)
|
||||
# emoji, actual_emoji = await self._wait_for_emoji(ctx)
|
||||
# except asyncio.TimeoutError:
|
||||
# await ctx.send("You didn't respond in time, please redo this command.")
|
||||
# return
|
||||
# await ctx.send("You didn't respond in time, please redo this command.")
|
||||
# return
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
await self.remove_react(message_id, role)
|
||||
@ -305,50 +295,50 @@ class ReactRestrict:
|
||||
for apprrole in roles:
|
||||
if apprrole in member.roles:
|
||||
return
|
||||
|
||||
|
||||
message = await self._get_message_from_channel(channel_id, message_id)
|
||||
await message.remove_reaction(emoji, member)
|
||||
|
||||
# try:
|
||||
# await member.add_roles(*roles)
|
||||
# except discord.Forbidden:
|
||||
# pass
|
||||
|
||||
# try:
|
||||
# await member.add_roles(*roles)
|
||||
# except discord.Forbidden:
|
||||
# pass
|
||||
#
|
||||
# async def on_raw_reaction_remove(self, emoji: discord.PartialReactionEmoji,
|
||||
# message_id: int, channel_id: int, user_id: int):
|
||||
# """
|
||||
# Event handler for long term reaction watching.
|
||||
|
||||
# :param discord.PartialReactionEmoji emoji:
|
||||
# :param int message_id:
|
||||
# :param int channel_id:
|
||||
# :param int user_id:
|
||||
# :return:
|
||||
# """
|
||||
# if emoji.is_custom_emoji():
|
||||
# emoji_id = emoji.id
|
||||
# else:
|
||||
# emoji_id = emoji.name
|
||||
|
||||
# has_reactrestrict, combos = await self.has_reactrestrict_combo(message_id, emoji_id)
|
||||
|
||||
# if not has_reactrestrict:
|
||||
# return
|
||||
|
||||
# try:
|
||||
# member = self._get_member(channel_id, user_id)
|
||||
# except LookupError:
|
||||
# return
|
||||
|
||||
# if member.bot:
|
||||
# return
|
||||
|
||||
# try:
|
||||
# roles = [self._get_role(member.guild, c.role_id) for c in combos]
|
||||
# except LookupError:
|
||||
# return
|
||||
|
||||
# try:
|
||||
# await member.remove_roles(*roles)
|
||||
# except discord.Forbidden:
|
||||
# pass
|
||||
# message_id: int, channel_id: int, user_id: int):
|
||||
# """
|
||||
# Event handler for long term reaction watching.
|
||||
#
|
||||
# :param discord.PartialReactionEmoji emoji:
|
||||
# :param int message_id:
|
||||
# :param int channel_id:
|
||||
# :param int user_id:
|
||||
# :return:
|
||||
# """
|
||||
# if emoji.is_custom_emoji():
|
||||
# emoji_id = emoji.id
|
||||
# else:
|
||||
# emoji_id = emoji.name
|
||||
#
|
||||
# has_reactrestrict, combos = await self.has_reactrestrict_combo(message_id, emoji_id)
|
||||
#
|
||||
# if not has_reactrestrict:
|
||||
# return
|
||||
#
|
||||
# try:
|
||||
# member = self._get_member(channel_id, user_id)
|
||||
# except LookupError:
|
||||
# return
|
||||
#
|
||||
# if member.bot:
|
||||
# return
|
||||
#
|
||||
# try:
|
||||
# roles = [self._get_role(member.guild, c.role_id) for c in combos]
|
||||
# except LookupError:
|
||||
# return
|
||||
#
|
||||
# try:
|
||||
# await member.remove_roles(*roles)
|
||||
# except discord.Forbidden:
|
||||
# pass
|
||||
|
9
recyclingplant/__init__.py
Normal file
9
recyclingplant/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from redbot.core import data_manager
|
||||
|
||||
from .recyclingplant import RecyclingPlant
|
||||
|
||||
|
||||
def setup(bot):
|
||||
plant = RecyclingPlant(bot)
|
||||
data_manager.load_bundled_data(plant, __file__)
|
||||
bot.add_cog(plant)
|
204
recyclingplant/data/junk.json
Normal file
204
recyclingplant/data/junk.json
Normal file
@ -0,0 +1,204 @@
|
||||
{
|
||||
"can": [
|
||||
{
|
||||
"object": "Apple Core",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Paper Cup",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Banana Peel",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Paper Bag",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Old Taco",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Newspaper",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Chewed Gum",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Polythene Bag",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Rotten Eggs",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Outdated Telephone Directory",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Stale Bread",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Used Notebook",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Sour Milk",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Old Textbook",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Week-Old Sandwich",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Paper Ball",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Leftovers",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Toy Car",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Fish Bones",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Superhero Costume",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Dirty Diaper",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Silcone Mould",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Mouldy Broccoli",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "TV Remote",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Withered Rose Bouquet",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Paper Plate",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Slimy Bacon",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Folders",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Fly Agaric Mushrooms",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Phone case",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Napkins",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Broken Dualshock 4 Controller",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Wax Paper",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "iPad",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Paint Can",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Glass Bottle",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Light Bulb",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Nintendo 3DS",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Styrofoam Container",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Flash Cards",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Motor Oil Can",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Candy Wrapper",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Waxed Cardboard",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Empty Bottle",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Used Toilet Paper",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Outdated Calendar",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Ceramic Mug",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Plastic Cup",
|
||||
"action": "recycle"
|
||||
},
|
||||
{
|
||||
"object": "Gift Wrapping",
|
||||
"action": "trash"
|
||||
},
|
||||
{
|
||||
"object": "Soda Bottle",
|
||||
"action": "recycle"
|
||||
}
|
||||
]
|
||||
}
|
21
recyclingplant/info.json
Normal file
21
recyclingplant/info.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy",
|
||||
"SnappyDragon"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Apply for a job at the recycling plant! Sort out the garbage!",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing RecyclingPlant. Start recycling today with `[p]load recyclingplant`, then `[p]recyclingplant`",
|
||||
"requirements": [],
|
||||
"short": "Apply for a job at the recycling plant!",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"environment",
|
||||
"games"
|
||||
]
|
||||
}
|
70
recyclingplant/recyclingplant.py
Normal file
70
recyclingplant/recyclingplant.py
Normal file
@ -0,0 +1,70 @@
|
||||
import asyncio
|
||||
import json
|
||||
import random
|
||||
|
||||
from redbot.core import bank
|
||||
from redbot.core import commands
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class RecyclingPlant(Cog):
|
||||
"""Apply for a job at the recycling plant!"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.path = str(cog_data_path(self)).replace('\\', '/')
|
||||
self.junk_path = self.path + "/bundled_data/junk.json"
|
||||
|
||||
with open(self.junk_path) as json_data:
|
||||
self.junk = json.load(json_data)
|
||||
|
||||
@commands.command(aliases=["recycle"])
|
||||
async def recyclingplant(self, ctx: commands.Context):
|
||||
"""Apply for a job at the recycling plant!"""
|
||||
x = 0
|
||||
reward = 0
|
||||
await ctx.send(
|
||||
'{0} has signed up for a shift at the Recycling Plant! Type ``exit`` to terminate it early.'.format(
|
||||
ctx.author.display_name))
|
||||
while x in range(0, 10):
|
||||
used = random.choice(self.junk['can'])
|
||||
if used['action'] == 'trash':
|
||||
opp = 'recycle'
|
||||
else:
|
||||
opp = 'trash'
|
||||
await ctx.send('``{}``! Will {} ``trash`` it or ``recycle`` it?'.format(used['object'],
|
||||
ctx.author.display_name))
|
||||
|
||||
def check(m):
|
||||
return m.author == ctx.author and m.channel == ctx.channel
|
||||
|
||||
try:
|
||||
answer = await self.bot.wait_for('message', timeout=120, check=check)
|
||||
except asyncio.TimeoutError:
|
||||
answer = None
|
||||
|
||||
if answer is None:
|
||||
await ctx.send('``{}`` fell down the conveyor belt to be sorted again!'.format(used['object']))
|
||||
elif answer.content.lower().strip() == used['action']:
|
||||
await ctx.send(
|
||||
'Congratulations! You put ``{}`` down the correct chute! (**+50**)'.format(used['object']))
|
||||
reward = reward + 50
|
||||
x += 1
|
||||
elif answer.content.lower().strip() == opp:
|
||||
await ctx.send('{}, you little brute, you put it down the wrong chute! (**-50**)'.format(
|
||||
ctx.author.display_name))
|
||||
reward = reward - 50
|
||||
elif answer.content.lower().strip() == 'exit':
|
||||
await ctx.send('{} has been relived of their duty.'.format(ctx.author.display_name))
|
||||
break
|
||||
else:
|
||||
await ctx.send('``{}`` fell down the conveyor belt to be sorted again!'.format(used['object']))
|
||||
else:
|
||||
if reward > 0:
|
||||
bank.deposit_credits(ctx.author, reward)
|
||||
await ctx.send(
|
||||
'{} been given **{} {}s** for your services.'.format(ctx.author.display_name, reward,
|
||||
bank.get_currency_name(ctx.guild)))
|
5
rpsls/__init__.py
Normal file
5
rpsls/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .rpsls import RPSLS
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(RPSLS(bot))
|
21
rpsls/info.json
Normal file
21
rpsls/info.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy",
|
||||
"SnappyDragon"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Play Rock Papers Scissor Lizard Spock by Sam Kass in Discord!",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing RPSLP. Get started with `[p]load rpsls`, then `[p]rpsls`",
|
||||
"requirements": [],
|
||||
"short": "Play Rock Papers Scissor Lizard Spock in Discord!",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"star trek",
|
||||
"games"
|
||||
]
|
||||
}
|
93
rpsls/rpsls.py
Normal file
93
rpsls/rpsls.py
Normal file
@ -0,0 +1,93 @@
|
||||
import asyncio
|
||||
import random
|
||||
|
||||
import discord
|
||||
from redbot.core import commands
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class RPSLS(Cog):
|
||||
"""Play Rock Paper Scissors Lizard Spock."""
|
||||
|
||||
weaknesses = {
|
||||
"rock": [
|
||||
"paper",
|
||||
"spock"
|
||||
],
|
||||
"paper": [
|
||||
"scissors",
|
||||
"lizard"
|
||||
],
|
||||
"scissors": [
|
||||
"spock",
|
||||
"rock"
|
||||
],
|
||||
"lizard": [
|
||||
"scissors",
|
||||
"rock"
|
||||
],
|
||||
"spock": [
|
||||
"paper",
|
||||
"lizard"
|
||||
]
|
||||
}
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command()
|
||||
async def rpsls(self, ctx: commands.Context, choice: str):
|
||||
"""
|
||||
Play Rock Paper Scissors Lizard Spock by Sam Kass in Discord!
|
||||
|
||||
Rules:
|
||||
Scissors cuts Paper
|
||||
Paper covers Rock
|
||||
Rock crushes Lizard
|
||||
Lizard poisons Spock
|
||||
Spock smashes Scissors
|
||||
Scissors decapitates Lizard
|
||||
Lizard eats Paper
|
||||
Paper disproves Spock
|
||||
Spock vaporizes Rock
|
||||
And as it has always Rock crushes Scissors
|
||||
"""
|
||||
|
||||
player_choice = choice.lower()
|
||||
player_emote = self.get_emote(player_choice)
|
||||
if player_emote is None:
|
||||
await ctx.maybe_send_embed("Invalid Choice")
|
||||
return
|
||||
|
||||
bot_choice = random.choice(list(self.weaknesses.keys()))
|
||||
bot_emote = self.get_emote(bot_choice)
|
||||
message = '{} vs. {}, who will win?'.format(player_emote, bot_emote)
|
||||
em = discord.Embed(description=message, color=discord.Color.blue())
|
||||
await ctx.send(embed=em)
|
||||
await asyncio.sleep(2)
|
||||
if player_choice in self.weaknesses[bot_choice]:
|
||||
message = 'You win! :sob:'
|
||||
em_color = discord.Color.green()
|
||||
elif bot_choice in self.weaknesses[player_choice]:
|
||||
message = 'I win! :smile:'
|
||||
em_color = discord.Color.red()
|
||||
else:
|
||||
message = 'It\'s a draw! :neutral_face:'
|
||||
em_color = discord.Color.blue()
|
||||
em = discord.Embed(description=message, color=em_color)
|
||||
await ctx.send(embed=em)
|
||||
|
||||
def get_emote(self, choice):
|
||||
if choice == 'rock':
|
||||
emote = ':moyai:'
|
||||
elif choice == 'spock':
|
||||
emote = ':vulcan:'
|
||||
elif choice == 'paper':
|
||||
emote = ':page_facing_up:'
|
||||
elif choice in ['scissors', 'lizard']:
|
||||
emote = ':{}:'.format(choice)
|
||||
else:
|
||||
emote = None
|
||||
return emote
|
5
sayurl/__init__.py
Normal file
5
sayurl/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .sayurl import SayUrl
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(SayUrl(bot))
|
19
sayurl/info..json
Normal file
19
sayurl/info..json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Convert any website into text and post it in chat",
|
||||
"hidden": true,
|
||||
"install_msg": "Thank you for installing SayUrl! Get started with `[p]load forcemention`, then `[p]help SayUrl",
|
||||
"requirements": ["html2text"],
|
||||
"short": "Convert URL to text",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"tools"
|
||||
]
|
||||
}
|
57
sayurl/sayurl.py
Normal file
57
sayurl/sayurl.py
Normal file
@ -0,0 +1,57 @@
|
||||
import aiohttp
|
||||
import html2text
|
||||
|
||||
from redbot.core import Config, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.utils.chat_formatting import pagify
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
async def fetch_url(session, url):
|
||||
async with session.get(url) as response:
|
||||
assert response.status == 200
|
||||
return await response.text()
|
||||
|
||||
|
||||
class SayUrl(Cog):
|
||||
"""
|
||||
V3 Cog Template
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_global = {}
|
||||
default_guild = {}
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.command()
|
||||
async def sayurl(self, ctx: commands.Context, url):
|
||||
"""
|
||||
Converts a URL to something readable
|
||||
|
||||
Works better on smaller websites
|
||||
"""
|
||||
|
||||
h = html2text.HTML2Text()
|
||||
h.ignore_links = True
|
||||
# h.ignore_images = True
|
||||
h.images_to_alt = True
|
||||
|
||||
h.escape_snob = True
|
||||
h.skip_internal_links = True
|
||||
h.ignore_tables = True
|
||||
h.single_line_break = True
|
||||
h.mark_code = True
|
||||
h.wrap_links = True
|
||||
h.ul_item_mark = '-'
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
site = await fetch_url(session, url)
|
||||
|
||||
for page in pagify(h.handle(site)):
|
||||
await ctx.send(page)
|
5
scp/__init__.py
Normal file
5
scp/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .scp import SCP
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(SCP(bot))
|
20
scp/info.json
Normal file
20
scp/info.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy",
|
||||
"SnappyDragon"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Look up SCP articles. Warning: Some of them may be too creepy or gruesome.",
|
||||
"hidden": false,
|
||||
"install_msg": "You are now connected to the SCP database. You may now proceed to access the data using `[p]load scp`, then `[p]help SCP`",
|
||||
"requirements": [],
|
||||
"short": "Look up SCP articles.",
|
||||
"tags": [
|
||||
"bobloy",
|
||||
"gruesom"
|
||||
]
|
||||
}
|
119
scp/scp.py
Normal file
119
scp/scp.py
Normal file
@ -0,0 +1,119 @@
|
||||
import discord
|
||||
from redbot.core import commands
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class SCP(Cog):
|
||||
"""Look up SCP articles. Warning: Some of them may be too creepy or gruesome."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command()
|
||||
async def scp(self, ctx: commands.Context, num: int):
|
||||
"""Look up SCP articles.
|
||||
|
||||
Warning: Some of them may be too creepy or gruesome.
|
||||
Reminder: You must specify a number between 1 and 4999.
|
||||
"""
|
||||
|
||||
# Thanks Shigbeard and Redjumpman for helping me!
|
||||
|
||||
if 0 < num <= 4999:
|
||||
msg = "http://www.scp-wiki.net/scp-{:03}".format(num)
|
||||
c = discord.Color.green()
|
||||
else:
|
||||
msg = "You must specify a number between 1 and 4999."
|
||||
c = discord.Color.red()
|
||||
|
||||
if ctx.embed_requested():
|
||||
await ctx.send(embed=discord.Embed(description=msg, color=c))
|
||||
else:
|
||||
await ctx.maybe_send_embed(msg)
|
||||
|
||||
@commands.command()
|
||||
async def scpj(self, ctx: commands.Context, joke: str):
|
||||
"""Look up SCP-Js.
|
||||
|
||||
Reminder: Enter the correct name or else the resultant page will be invalid.
|
||||
Use 001, etc. in case of numbers less than 100.
|
||||
"""
|
||||
|
||||
msg = "http://www.scp-wiki.net/scp-{}-j".format(joke)
|
||||
await ctx.maybe_send_embed(msg)
|
||||
|
||||
@commands.command()
|
||||
async def scparc(self, ctx: commands.Context, num: int):
|
||||
"""Look up SCP archives.
|
||||
|
||||
Warning: Some of them may be too creepy or gruesome."""
|
||||
valid_archive = (
|
||||
13, 48, 51, 89, 91, 112, 132, 138, 157, 186, 232, 234,
|
||||
244, 252, 257, 338, 356, 361, 400, 406, 503, 515, 517,
|
||||
578, 728, 744, 776, 784, 837, 922, 987, 1023)
|
||||
if num in valid_archive:
|
||||
msg = "http://www.scp-wiki.net/scp-{:03}-arc".format(num)
|
||||
c = discord.Color.green()
|
||||
em = discord.Embed(description=msg, color=c)
|
||||
else:
|
||||
ttl = "You must specify a valid archive number."
|
||||
msg = "{}".format(valid_archive)
|
||||
c = discord.Color.red()
|
||||
|
||||
em = discord.Embed(title=ttl, description=msg, color=c)
|
||||
|
||||
if ctx.embed_requested():
|
||||
await ctx.send(embed=em)
|
||||
else:
|
||||
await ctx.maybe_send_embed(msg)
|
||||
|
||||
@commands.command()
|
||||
async def scpex(self, ctx: commands.Context, num: int):
|
||||
"""Look up explained SCP articles.
|
||||
|
||||
Warning: Some of them may be too creepy or gruesome.
|
||||
"""
|
||||
|
||||
valid_archive = (711, 920, 1841, 1851, 1974, 2600, 4023, 8900)
|
||||
if num in valid_archive:
|
||||
msg = "http://www.scp-wiki.net/scp-{:03}-ex".format(num)
|
||||
c = discord.Color.green()
|
||||
em = discord.Embed(description=msg, color=c)
|
||||
else:
|
||||
ttl = "You must specify a valid archive number."
|
||||
msg = "{}".format(valid_archive)
|
||||
c = discord.Color.red()
|
||||
|
||||
em = discord.Embed(title=ttl, description=msg, color=c)
|
||||
|
||||
if ctx.embed_requested():
|
||||
await ctx.send(embed=em)
|
||||
else:
|
||||
await ctx.maybe_send_embed(msg)
|
||||
|
||||
@commands.command()
|
||||
async def anomalousitems(self, ctx: commands.Context):
|
||||
"""Look through the log of anomalous items."""
|
||||
|
||||
msg = "http://www.scp-wiki.net/log-of-anomalous-items"
|
||||
await ctx.maybe_send_embed(msg)
|
||||
|
||||
@commands.command()
|
||||
async def extranormalevents(self, ctx: commands.Context):
|
||||
"""Look through the log of extranormal events."""
|
||||
|
||||
msg = "http://www.scp-wiki.net/log-of-extranormal-events"
|
||||
await ctx.maybe_send_embed(msg)
|
||||
|
||||
@commands.command()
|
||||
async def unexplainedlocations(self, ctx: commands.Context):
|
||||
"""Look through the log of unexplained locations."""
|
||||
|
||||
msg = "http://www.scp-wiki.net/log-of-unexplained-locations"
|
||||
await ctx.maybe_send_embed(msg)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(SCP(bot))
|
@ -1,4 +1,4 @@
|
||||
from .werewolf import Werewolf
|
||||
from .secrethitler import Werewolf
|
||||
|
||||
|
||||
def setup(bot):
|
||||
|
@ -1,9 +1,9 @@
|
||||
import asyncio
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
from redbot.core import Config
|
||||
|
||||
from redbot.core import Config, commands
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
@ -33,7 +33,7 @@ class Werewolf:
|
||||
Base command for this cog. Check help for the commands list.
|
||||
"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
pass
|
||||
|
||||
@ww.command()
|
||||
async def new(self, ctx, game_code):
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user