Merge branch 'master' into fifo_develop

pull/145/head
bobloy 4 years ago
commit bed6cf8bb7

@ -14,11 +14,13 @@ Cog Function
| exclusiverole | **Alpha** | <details><summary>Prevent certain roles from getting any other roles</summary>Fully functional, but pretty simple</details> | | exclusiverole | **Alpha** | <details><summary>Prevent certain roles from getting any other roles</summary>Fully functional, but pretty simple</details> |
| fifo | **Alpha** | <details><summary>Schedule commands to be run at certain times or intervals</summary>Just released, please report bugs as you find them. Only works for bot owner for now</details> | | fifo | **Alpha** | <details><summary>Schedule commands to be run at certain times or intervals</summary>Just released, please report bugs as you find them. Only works for bot owner for now</details> |
| fight | **Incomplete** | <details><summary>Organize bracket tournaments within discord</summary>Still in-progress, a massive project</details> | | fight | **Incomplete** | <details><summary>Organize bracket tournaments within discord</summary>Still in-progress, a massive project</details> |
| firstmessage | **Release** | <details><summary>Simple cog to provide a jump link to the first message in a channel/summary>Just released, please report bugs as you find them.</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> | | 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> | | forcemention | **Alpha** | <details><summary>Mentions unmentionable roles</summary>Very simple cog, mention doesn't persist</details> |
| hangman | **Beta** | <details><summary>Play a game of hangman</summary>Some visual glitches and needs more customization</details> | | hangman | **Beta** | <details><summary>Play a game of hangman</summary>Some visual glitches and needs more customization</details> |
| howdoi | **Incomplete** | <details><summary>Ask coding questions and get results from StackExchange</summary>Not yet functional</details> | | howdoi | **Incomplete** | <details><summary>Ask coding questions and get results from StackExchange</summary>Not yet functional</details> |
| infochannel | **Beta** | <details><summary>Create a channel to display server info</summary>Just released, please report bugs</details> | | infochannel | **Beta** | <details><summary>Create a channel to display server info</summary>Due to rate limits, this does not update as often as it once did</details> |
| isitdown | **Beta** | <details><summary>Check if a website/url is down</summary>Just released, please report bugs</details> |
| launchlib | **Beta** | <details><summary>Access rocket launch data</summary>Just released, please report bugs</details> | | launchlib | **Beta** | <details><summary>Access rocket launch data</summary>Just released, please report bugs</details> |
| leaver | **Beta** | <details><summary>Send a message in a channel when a user leaves the server</summary>Seems to be functional, please report any bugs or suggestions</details> | | leaver | **Beta** | <details><summary>Send a message in a channel when a user leaves the server</summary>Seems to be functional, please report any bugs or suggestions</details> |
| lovecalculator | **Alpha** | <details><summary>Calculate the love between two users</summary>[Snap-Ons] Just updated to V3</details> | | lovecalculator | **Alpha** | <details><summary>Calculate the love between two users</summary>[Snap-Ons] Just updated to V3</details> |
@ -38,7 +40,7 @@ Cog Function
| unicode | **Alpha** | <details><summary>Encode and Decode unicode characters</summary>[Snap-Ons] Just updated to V3</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> | | 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) Check out *Deprecated* my V2 cogs at [Fox-Cogs v2](https://github.com/bobloy/Fox-Cogs)
# Installation # Installation
### Recommended - Built-in Downloader ### Recommended - Built-in Downloader

@ -1,8 +1,13 @@
"""Module to manage audio trivia sessions.""" """Module to manage audio trivia sessions."""
import asyncio import asyncio
import logging
import lavalink import lavalink
from lavalink.enums import LoadType
from redbot.cogs.trivia import TriviaSession from redbot.cogs.trivia import TriviaSession
from redbot.core.utils.chat_formatting import bold
log = logging.getLogger("red.fox_v3.audiotrivia.audiosession")
class AudioSession(TriviaSession): class AudioSession(TriviaSession):
@ -36,8 +41,9 @@ class AudioSession(TriviaSession):
self.count += 1 self.count += 1
await self.player.stop() await self.player.stop()
msg = "**Question number {}!**\n\nName this audio!".format(self.count) msg = bold(f"Question number {self.count}!") + "\n\nName this audio!"
await self.ctx.send(msg) await self.ctx.maybe_send_embed(msg)
log.debug(f"Audio question: {question}")
# print("Audio question: {}".format(question)) # print("Audio question: {}".format(question))
# await self.ctx.invoke(self.audio.play(ctx=self.ctx, query=question)) # await self.ctx.invoke(self.audio.play(ctx=self.ctx, query=question))
@ -45,18 +51,28 @@ class AudioSession(TriviaSession):
# await self.ctx.invoke(self.player.play, query=question) # await self.ctx.invoke(self.player.play, query=question)
query = question.strip("<>") query = question.strip("<>")
tracks = await self.player.get_tracks(query) load_result = await self.player.load_tracks(query)
seconds = tracks[0].length / 1000 log.debug(f"{load_result.load_type=}")
if load_result.has_error or load_result.load_type != LoadType.TRACK_LOADED:
await self.ctx.maybe_send_embed(f"Track has error, skipping. See logs for details")
log.info(f"Track has error: {load_result.exception_message}")
continue # Skip tracks with error
tracks = load_result.tracks
track = tracks[0]
seconds = track.length / 1000
if self.settings["repeat"] and seconds < delay: if self.settings["repeat"] and seconds < delay:
# Append it until it's longer than the delay
tot_length = seconds + 0 tot_length = seconds + 0
while tot_length < delay: while tot_length < delay:
self.player.add(self.ctx.author, tracks[0]) self.player.add(self.ctx.author, track)
tot_length += seconds tot_length += seconds
else: else:
self.player.add(self.ctx.author, tracks[0]) self.player.add(self.ctx.author, track)
if not self.player.current: if not self.player.current:
log.debug("Pressing play")
await self.player.play() await self.player.play()
continue_ = await self.wait_for_answer(answers, delay, timeout) continue_ = await self.wait_for_answer(answers, delay, timeout)

@ -1,4 +1,5 @@
import datetime import datetime
import logging
import pathlib import pathlib
from typing import List from typing import List
@ -15,7 +16,7 @@ from redbot.core.utils.chat_formatting import box
from .audiosession import AudioSession from .audiosession import AudioSession
# from redbot.cogs.audio.utils import userlimit log = logging.getLogger("red.fox_v3.audiotrivia")
class AudioTrivia(Trivia): class AudioTrivia(Trivia):
@ -83,24 +84,24 @@ class AudioTrivia(Trivia):
self.audio: Audio = self.bot.get_cog("Audio") self.audio: Audio = self.bot.get_cog("Audio")
if self.audio is None: if self.audio is None:
await ctx.send("Audio is not loaded. Load it and try again") await ctx.maybe_send_embed("Audio is not loaded. Load it and try again")
return return
categories = [c.lower() for c in categories] categories = [c.lower() for c in categories]
session = self._get_trivia_session(ctx.channel) session = self._get_trivia_session(ctx.channel)
if session is not None: if session is not None:
await ctx.send("There is already an ongoing trivia session in this channel.") await ctx.maybe_send_embed("There is already an ongoing trivia session in this channel.")
return return
status = await self.audio.config.status() status = await self.audio.config.status()
notify = await self.audio.config.guild(ctx.guild).notify() notify = await self.audio.config.guild(ctx.guild).notify()
if status: if status:
await ctx.send( await ctx.maybe_send_embed(
f"It is recommended to disable audio status with `{ctx.prefix}audioset status`" f"It is recommended to disable audio status with `{ctx.prefix}audioset status`"
) )
if notify: if notify:
await ctx.send( await ctx.maybe_send_embed(
f"It is recommended to disable audio notify with `{ctx.prefix}audioset notify`" f"It is recommended to disable audio notify with `{ctx.prefix}audioset notify`"
) )
@ -109,12 +110,12 @@ class AudioTrivia(Trivia):
if not ctx.author.voice.channel.permissions_for( if not ctx.author.voice.channel.permissions_for(
ctx.me ctx.me
).connect or self.audio.is_vc_full(ctx.author.voice.channel): ).connect or self.audio.is_vc_full(ctx.author.voice.channel):
return await ctx.send("I don't have permission to connect to your channel.") return await ctx.maybe_send_embed("I don't have permission to connect to your channel.")
await lavalink.connect(ctx.author.voice.channel) await lavalink.connect(ctx.author.voice.channel)
lavaplayer = lavalink.get_player(ctx.guild.id) lavaplayer = lavalink.get_player(ctx.guild.id)
lavaplayer.store("connect", datetime.datetime.utcnow()) lavaplayer.store("connect", datetime.datetime.utcnow())
except AttributeError: except AttributeError:
return await ctx.send("Connect to a voice channel first.") return await ctx.maybe_send_embed("Connect to a voice channel first.")
lavaplayer = lavalink.get_player(ctx.guild.id) lavaplayer = lavalink.get_player(ctx.guild.id)
lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno
@ -122,7 +123,7 @@ class AudioTrivia(Trivia):
await self.audio.set_player_settings(ctx) await self.audio.set_player_settings(ctx)
if not ctx.author.voice or ctx.author.voice.channel != lavaplayer.channel: if not ctx.author.voice or ctx.author.voice.channel != lavaplayer.channel:
return await ctx.send( return await ctx.maybe_send_embed(
"You must be in the voice channel to use the audiotrivia command." "You must be in the voice channel to use the audiotrivia command."
) )
@ -134,13 +135,13 @@ class AudioTrivia(Trivia):
try: try:
dict_ = self.get_audio_list(category) dict_ = self.get_audio_list(category)
except FileNotFoundError: except FileNotFoundError:
await ctx.send( await ctx.maybe_send_embed(
"Invalid category `{0}`. See `{1}audiotrivia list`" "Invalid category `{0}`. See `{1}audiotrivia list`"
" for a list of trivia categories." " for a list of trivia categories."
"".format(category, ctx.prefix) "".format(category, ctx.prefix)
) )
except InvalidListError: except InvalidListError:
await ctx.send( await ctx.maybe_send_embed(
"There was an error parsing the trivia list for" "There was an error parsing the trivia list for"
" the `{}` category. It may be formatted" " the `{}` category. It may be formatted"
" incorrectly.".format(category) " incorrectly.".format(category)
@ -151,7 +152,7 @@ class AudioTrivia(Trivia):
continue continue
return return
if not trivia_dict: if not trivia_dict:
await ctx.send( await ctx.maybe_send_embed(
"The trivia list was parsed successfully, however it appears to be empty!" "The trivia list was parsed successfully, however it appears to be empty!"
) )
return return
@ -166,7 +167,10 @@ class AudioTrivia(Trivia):
# Delay in audiosettings overwrites delay in settings # Delay in audiosettings overwrites delay in settings
combined_settings = {**settings, **audiosettings} combined_settings = {**settings, **audiosettings}
session = AudioSession.start( session = AudioSession.start(
ctx=ctx, question_list=trivia_dict, settings=combined_settings, player=lavaplayer, ctx=ctx,
question_list=trivia_dict,
settings=combined_settings,
player=lavaplayer,
) )
self.trivia_sessions.append(session) self.trivia_sessions.append(session)
LOG.debug("New audio trivia session; #%s in %d", ctx.channel, ctx.guild.id) LOG.debug("New audio trivia session; #%s in %d", ctx.channel, ctx.guild.id)

@ -1,13 +1,13 @@
AUTHOR: Plab AUTHOR: Plab
https://www.youtube.com/watch?v=--bWm9hhoZo: https://www.youtube.com/watch?v=f9O2Rjn1azc:
- Transistor - Transistor
https://www.youtube.com/watch?v=-4nCbgayZNE: https://www.youtube.com/watch?v=PgUhYFkVdSY:
- Dark Cloud 2 - Dark Cloud 2
- Dark Cloud II - Dark Cloud II
https://www.youtube.com/watch?v=-64NlME4lJU: https://www.youtube.com/watch?v=1T1RZttyMwU:
- Mega Man 7 - Mega Man 7
- Mega Man VII - Mega Man VII
https://www.youtube.com/watch?v=-AesqnudNuw: https://www.youtube.com/watch?v=AdDbbzuq1vY:
- Mega Man 9 - Mega Man 9
- Mega Man IX - Mega Man IX
https://www.youtube.com/watch?v=-BmGDtP2t7M: https://www.youtube.com/watch?v=-BmGDtP2t7M:

@ -7,6 +7,7 @@ from discord.ext.commands.view import StringView
from redbot.core import Config, checks, commands from redbot.core import Config, checks, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import box, pagify from redbot.core.utils.chat_formatting import box, pagify
from redbot.core.utils.mod import get_audit_reason
log = logging.getLogger("red.fox_v3.ccrole") log = logging.getLogger("red.fox_v3.ccrole")
@ -358,23 +359,24 @@ class CCRole(commands.Cog):
else: else:
target = message.author target = message.author
reason = get_audit_reason(message.author)
if cmd["aroles"]: if cmd["aroles"]:
arole_list = [ arole_list = [
discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd["aroles"] discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd["aroles"]
] ]
try: try:
await target.add_roles(*arole_list) await target.add_roles(*arole_list, reason=reason)
except discord.Forbidden: except discord.Forbidden:
log.exception(f"Permission error: Unable to add roles") log.exception(f"Permission error: Unable to add roles")
await ctx.send("Permission error: Unable to add roles") await ctx.send("Permission error: Unable to add roles")
await asyncio.sleep(1)
if cmd["rroles"]: if cmd["rroles"]:
rrole_list = [ rrole_list = [
discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd["rroles"] discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd["rroles"]
] ]
try: try:
await target.remove_roles(*rrole_list) await target.remove_roles(*rrole_list, reason=reason)
except discord.Forbidden: except discord.Forbidden:
log.exception(f"Permission error: Unable to remove roles") log.exception(f"Permission error: Unable to remove roles")
await ctx.send("Permission error: Unable to remove roles") await ctx.send("Permission error: Unable to remove roles")

@ -29,7 +29,7 @@ Chatter by default uses spaCy's `en_core_web_md` training model, which is ~50 MB
Chatter can potential use spaCy's `en_core_web_lg` training model, which is ~800 MB Chatter can potential use spaCy's `en_core_web_lg` training model, which is ~800 MB
Chatter uses as sqlite database that can potentially take up a large amount os disk space, Chatter uses as sqlite database that can potentially take up a large amount of disk space,
depending on how much training Chatter has done. depending on how much training Chatter has done.
The sqlite database can be safely deleted at any time. Deletion will only erase training data. The sqlite database can be safely deleted at any time. Deletion will only erase training data.
@ -50,7 +50,9 @@ Linux is a bit easier, but only tested on Debian and Ubuntu.
## Windows Prerequisites ## Windows Prerequisites
Install these on your windows machine before attempting the installation **Requires 64 Bit Python to continue on Windows.**
Install these on your windows machine before attempting the installation:
[Visual Studio C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) [Visual Studio C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
@ -83,6 +85,7 @@ pip install --no-deps "chatterbot>=1.1"
#### Step 3: Load Chatter #### Step 3: Load Chatter
``` ```
[p]repo add Fox https://github.com/bobloy/Fox-V3 # If you didn't already do this in step 1
[p]cog install Fox chatter [p]cog install Fox chatter
[p]load chatter [p]load chatter
``` ```
@ -92,7 +95,7 @@ pip install --no-deps "chatterbot>=1.1"
#### Step 1: Built-in Downloader #### Step 1: Built-in Downloader
``` ```
[p]cog install Chatter [p]cog install <Fox> Chatter
``` ```
#### Step 2: Install Requirements #### Step 2: Install Requirements

@ -463,7 +463,7 @@ class Chatter(Cog):
# Thank you Cog-Creators # Thank you Cog-Creators
channel: discord.TextChannel = message.channel channel: discord.TextChannel = message.channel
if channel.id == await self.config.guild(guild).chatchannel(): if guild is not None and channel.id == await self.config.guild(guild).chatchannel():
pass # good to go pass # good to go
else: else:
when_mentionables = commands.when_mentioned(self.bot, message) when_mentionables = commands.when_mentioned(self.bot, message)

@ -10,6 +10,7 @@ from apscheduler.schedulers.base import STATE_PAUSED, STATE_RUNNING
from redbot.core import Config, checks, commands from redbot.core import Config, checks, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.commands import TimedeltaConverter from redbot.core.commands import TimedeltaConverter
from redbot.core.utils.chat_formatting import pagify
from .datetime_cron_converters import CronConverter, DatetimeConverter, TimezoneConverter from .datetime_cron_converters import CronConverter, DatetimeConverter, TimezoneConverter
from .task import Task from .task import Task
@ -306,6 +307,10 @@ class FIFO(commands.Cog):
out += f"{task_name}: {task_data}\n" out += f"{task_name}: {task_data}\n"
if out: if out:
if len(out) > 2000:
for page in pagify(out):
await ctx.maybe_send_embed(page)
else:
await ctx.maybe_send_embed(out) await ctx.maybe_send_embed(out)
else: else:
await ctx.maybe_send_embed("No tasks to list") await ctx.maybe_send_embed("No tasks to list")

@ -16,6 +16,7 @@
"bobloy", "bobloy",
"utilities", "utilities",
"tool", "tool",
"tools",
"roles", "roles",
"schedule", "schedule",
"cron", "cron",

@ -0,0 +1,5 @@
from .firstmessage import FirstMessage
async def setup(bot):
bot.add_cog(FirstMessage(bot))

@ -0,0 +1,49 @@
import logging
import discord
from redbot.core import Config, commands
from redbot.core.bot import Red
log = logging.getLogger("red.fox_v3.firstmessage")
class FirstMessage(commands.Cog):
"""
Provides a link to the first message in the provided channel
"""
def __init__(self, bot: Red):
super().__init__()
self.bot = bot
self.config = Config.get_conf(
self, identifier=701051141151167710111511597103101, force_registration=True
)
default_guild = {}
self.config.register_guild(**default_guild)
async def red_delete_data_for_user(self, **kwargs):
"""Nothing to delete"""
return
@commands.command()
async def firstmessage(self, ctx: commands.Context, channel: discord.TextChannel = None):
"""
Provide a link to the first message in current or provided channel.
"""
if channel is None:
channel = ctx.channel
try:
message: discord.Message = (
await channel.history(limit=1, oldest_first=True).flatten()
)[0]
except (discord.Forbidden, discord.HTTPException):
log.exception(f"Unable to read message history for {channel.id=}")
await ctx.maybe_send_embed("Unable to read message history for that channel")
return
em = discord.Embed(description=f"[First Message in {channel.mention}]({message.jump_url})")
em.set_author(name=message.author.display_name, icon_url=message.author.avatar_url)
await ctx.send(embed=em)

@ -0,0 +1,17 @@
{
"author": [
"Bobloy"
],
"min_bot_version": "3.4.0",
"description": "Simple cog to jump to the first message of a channel easily",
"hidden": false,
"install_msg": "Thank you for installing FirstMessage.\nGet started with `[p]load firstmessage`, then `[p]help FirstMessage`",
"short": "Simple cog to jump to first message of a channel",
"end_user_data_statement": "This cog does not store any End User Data",
"tags": [
"bobloy",
"utilities",
"tool",
"tools"
]
}

@ -6,4 +6,3 @@ def setup(bot):
n = Hangman(bot) n = Hangman(bot)
data_manager.bundled_data_path(n) data_manager.bundled_data_path(n)
bot.add_cog(n) bot.add_cog(n)
bot.add_listener(n.on_react, "on_reaction_add")

@ -50,27 +50,27 @@ class Hangman(Cog):
theface = await self.config.guild(guild).theface() theface = await self.config.guild(guild).theface()
self.hanglist[guild] = ( self.hanglist[guild] = (
"""> """>
\_________ \\_________
|/ |/
| |
| |
| |
| |
| |
|\___ |\\___
""", """,
"""> """>
\_________ \\_________
|/ | |/ |
| |
| |
| |
| |
| |
|\___ |\\___
H""", H""",
"""> """>
\_________ \\_________
|/ | |/ |
| """ | """
+ theface + theface
@ -79,10 +79,10 @@ class Hangman(Cog):
| |
| |
| |
|\___ |\\___
HA""", HA""",
"""> """>
\________ \\________
|/ | |/ |
| """ | """
+ theface + theface
@ -91,10 +91,10 @@ class Hangman(Cog):
| | | |
| |
| |
|\___ |\\___
HAN""", HAN""",
"""> """>
\_________ \\_________
|/ | |/ |
| """ | """
+ theface + theface
@ -103,43 +103,43 @@ class Hangman(Cog):
| | | |
| |
| |
|\___ |\\___
HANG""", HANG""",
"""> """>
\_________ \\_________
|/ | |/ |
| """ | """
+ theface + theface
+ """ + """
| /|\ | /|\\
| | | |
| |
| |
|\___ |\\___
HANGM""", HANGM""",
"""> """>
\________ \\________
|/ | |/ |
| """ | """
+ theface + theface
+ """ + """
| /|\ | /|\\
| | | |
| / | /
| |
|\___ |\\___
HANGMA""", HANGMA""",
"""> """>
\________ \\________
|/ | |/ |
| """ | """
+ theface + theface
+ """ + """
| /|\ | /|\\
| | | |
| / \ | / \\
| |
|\___ |\\___
HANGMAN""", HANGMAN""",
) )
@ -255,7 +255,7 @@ class Hangman(Cog):
elif i in self.the_data[guild]["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": elif i in self.the_data[guild]["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
out_str += "__" + i + "__ " out_str += "__" + i + "__ "
else: else:
out_str += "**\_** " out_str += "**\\_** "
self.winbool[guild] = False self.winbool[guild] = False
return out_str return out_str
@ -286,7 +286,7 @@ class Hangman(Cog):
await self._reprintgame(message) await self._reprintgame(message)
@commands.Cog.listener() @commands.Cog.listener("on_reaction_add")
async def on_react(self, reaction, user: Union[discord.User, discord.Member]): async def on_react(self, reaction, user: Union[discord.User, discord.Member]):
"""Thanks to flapjack reactpoll for guidelines """Thanks to flapjack reactpoll for guidelines
https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py""" https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py"""

@ -0,0 +1,5 @@
from .isitdown import IsItDown
async def setup(bot):
bot.add_cog(IsItDown(bot))

@ -0,0 +1,17 @@
{
"author": [
"Bobloy"
],
"min_bot_version": "3.4.0",
"description": "Check if a website/url is down using the https://isitdown.site/ api",
"hidden": false,
"install_msg": "Thank you for installing IsItDown.\nGet started with `[p]load isitdown`, then `[p]help IsItDown`",
"short": "Check if a website/url is down",
"end_user_data_statement": "This cog does not store any End User Data",
"tags": [
"bobloy",
"utilities",
"tool",
"tools"
]
}

@ -0,0 +1,58 @@
import logging
import re
import aiohttp
from redbot.core import Config, commands
from redbot.core.bot import Red
log = logging.getLogger("red.fox_v3.isitdown")
class IsItDown(commands.Cog):
"""
Cog Description
Less important information about the cog
"""
def __init__(self, bot: Red):
super().__init__()
self.bot = bot
self.config = Config.get_conf(self, identifier=0, force_registration=True)
default_guild = {"iids": []} # List of tuple pairs (channel_id, website)
self.config.register_guild(**default_guild)
async def red_delete_data_for_user(self, **kwargs):
"""Nothing to delete"""
return
@commands.command(alias=["iid"])
async def isitdown(self, ctx: commands.Context, url_to_check):
"""
Check if the provided url is down
Alias: iid
"""
try:
resp = await self._check_if_down(url_to_check)
except AssertionError:
await ctx.maybe_send_embed("Invalid URL provided. Make sure not to include `http://`")
return
if resp["isitdown"]:
await ctx.maybe_send_embed(f"{url_to_check} is DOWN!")
else:
await ctx.maybe_send_embed(f"{url_to_check} is UP!")
async def _check_if_down(self, url_to_check):
url = re.compile(r"https?://(www\.)?")
url.sub("", url_to_check).strip().strip("/")
url = f"https://isitdown.site/api/v3/{url}"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
assert response.status == 200
resp = await response.json()
return resp

@ -22,7 +22,9 @@ class LaunchLib(commands.Cog):
def __init__(self, bot: Red): def __init__(self, bot: Red):
super().__init__() super().__init__()
self.bot = bot self.bot = bot
self.config = Config.get_conf(self, identifier=0, force_registration=True) self.config = Config.get_conf(
self, identifier=7697117110991047610598, force_registration=True
)
default_guild = {} default_guild = {}

@ -75,9 +75,7 @@ class LastSeen(Cog):
else: else:
last_seen = await self.config.member(member).seen() last_seen = await self.config.member(member).seen()
if last_seen is None: if last_seen is None:
await ctx.maybe_send_embed( await ctx.maybe_send_embed("I've never seen this user")
embed=discord.Embed(description="I've never seen this user")
)
return return
last_seen = self.get_date_time(last_seen) last_seen = self.get_date_time(last_seen)

@ -360,7 +360,9 @@ class PlantTycoon(commands.Cog):
``{0}prune``: Prune your plant.\n""" ``{0}prune``: Prune your plant.\n"""
em = discord.Embed( em = discord.Embed(
title=title, description=description.format(prefix), color=discord.Color.green(), title=title,
description=description.format(prefix),
color=discord.Color.green(),
) )
em.set_thumbnail(url="https://image.prntscr.com/image/AW7GuFIBSeyEgkR2W3SeiQ.png") em.set_thumbnail(url="https://image.prntscr.com/image/AW7GuFIBSeyEgkR2W3SeiQ.png")
em.set_footer( em.set_footer(
@ -525,7 +527,8 @@ class PlantTycoon(commands.Cog):
if t: if t:
em = discord.Embed( em = discord.Embed(
title="Plant statistics of {}".format(plant["name"]), color=discord.Color.green(), title="Plant statistics of {}".format(plant["name"]),
color=discord.Color.green(),
) )
em.set_thumbnail(url=plant["image"]) em.set_thumbnail(url=plant["image"])
em.add_field(name="**Name**", value=plant["name"]) em.add_field(name="**Name**", value=plant["name"])
@ -583,7 +586,8 @@ class PlantTycoon(commands.Cog):
author = ctx.author author = ctx.author
if product is None: if product is None:
em = discord.Embed( em = discord.Embed(
title="All gardening supplies that you can buy:", color=discord.Color.green(), title="All gardening supplies that you can buy:",
color=discord.Color.green(),
) )
for pd in self.products: for pd in self.products:
em.add_field( em.add_field(
@ -616,8 +620,11 @@ class PlantTycoon(commands.Cog):
await gardener.save_gardener() await gardener.save_gardener()
message = "You bought {}.".format(product.lower()) message = "You bought {}.".format(product.lower())
else: else:
message = "You don't have enough Thneeds. You have {}, but need {}.".format( message = (
gardener.points, self.products[product.lower()]["cost"] * amount, "You don't have enough Thneeds. You have {}, but need {}.".format(
gardener.points,
self.products[product.lower()]["cost"] * amount,
)
) )
else: else:
message = "I don't have this product." message = "I don't have this product."

@ -1,3 +1,4 @@
import logging
from typing import List, Union from typing import List, Union
import discord import discord
@ -5,6 +6,8 @@ from redbot.core import Config, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.commands import Cog from redbot.core.commands import Cog
log = logging.getLogger("red.fox_v3.reactrestrict")
class ReactRestrictCombo: class ReactRestrictCombo:
def __init__(self, message_id, role_id): def __init__(self, message_id, role_id):
@ -131,10 +134,12 @@ class ReactRestrict(Cog):
If no such channel or member can be found. If no such channel or member can be found.
""" """
channel = self.bot.get_channel(channel_id) channel = self.bot.get_channel(channel_id)
if channel is None:
raise LookupError("no channel found.")
try: try:
member = channel.guild.get_member(user_id) member = channel.guild.get_member(user_id)
except AttributeError as e: except AttributeError as e:
raise LookupError("No channel found.") from e raise LookupError("No member found.") from e
if member is None: if member is None:
raise LookupError("No member found.") raise LookupError("No member found.")
@ -168,7 +173,7 @@ class ReactRestrict(Cog):
""" """
channel = self.bot.get_channel(channel_id) channel = self.bot.get_channel(channel_id)
try: try:
return await channel.get_message(message_id) return await channel.fetch_message(message_id)
except discord.NotFound: except discord.NotFound:
pass pass
except AttributeError: # VoiceChannel object has no attribute 'get_message' except AttributeError: # VoiceChannel object has no attribute 'get_message'
@ -186,9 +191,11 @@ class ReactRestrict(Cog):
:param message_id: :param message_id:
:return: :return:
""" """
for channel in ctx.guild.channels:
guild: discord.Guild = ctx.guild
for channel in guild.text_channels:
try: try:
return await channel.get_message(message_id) return await channel.fetch_message(message_id)
except discord.NotFound: except discord.NotFound:
pass pass
except AttributeError: # VoiceChannel object has no attribute 'get_message' except AttributeError: # VoiceChannel object has no attribute 'get_message'
@ -232,7 +239,7 @@ class ReactRestrict(Cog):
# noinspection PyTypeChecker # noinspection PyTypeChecker
await self.add_reactrestrict(message_id, role) await self.add_reactrestrict(message_id, role)
await ctx.maybe_send_embed("Message|Role combo added.") await ctx.maybe_send_embed("Message|Role restriction added.")
@reactrestrict.command() @reactrestrict.command()
async def remove(self, ctx: commands.Context, message_id: int, role: discord.Role): async def remove(self, ctx: commands.Context, message_id: int, role: discord.Role):
@ -248,37 +255,38 @@ class ReactRestrict(Cog):
# noinspection PyTypeChecker # noinspection PyTypeChecker
await self.remove_react(message_id, role) await self.remove_react(message_id, role)
await ctx.send("Reaction removed.") await ctx.send("React restriction removed.")
@commands.Cog.listener() @commands.Cog.listener()
async def on_raw_reaction_add( async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent):
self, emoji: discord.PartialEmoji, message_id: int, channel_id: int, user_id: int
):
""" """
Event handler for long term reaction watching. 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 emoji = payload.emoji
else: message_id = payload.message_id
emoji_id = emoji.name channel_id = payload.channel_id
user_id = payload.user_id
# if emoji.is_custom_emoji():
# emoji_id = emoji.id
# else:
# emoji_id = emoji.name
has_reactrestrict, combos = await self.has_reactrestrict_combo(message_id) has_reactrestrict, combos = await self.has_reactrestrict_combo(message_id)
if not has_reactrestrict: if not has_reactrestrict:
log.debug("Message not react restricted")
return return
try: try:
member = self._get_member(channel_id, user_id) member = self._get_member(channel_id, user_id)
except LookupError: except LookupError:
log.exception("Unable to get member from guild")
return return
if member.bot: if member.bot:
log.debug("Won't remove reactions added by bots")
return return
if await self.bot.cog_disabled_in_guild(self, member.guild): if await self.bot.cog_disabled_in_guild(self, member.guild):
@ -287,14 +295,19 @@ class ReactRestrict(Cog):
try: try:
roles = [self._get_role(member.guild, c.role_id) for c in combos] roles = [self._get_role(member.guild, c.role_id) for c in combos]
except LookupError: except LookupError:
log.exception("Couldn't get approved roles from combos")
return return
for apprrole in roles: for apprrole in roles:
if apprrole in member.roles: if apprrole in member.roles:
log.debug("Has approved role")
return return
message = await self._get_message_from_channel(channel_id, message_id) message = await self._get_message_from_channel(channel_id, message_id)
try:
await message.remove_reaction(emoji, member) await message.remove_reaction(emoji, member)
except (discord.Forbidden, discord.NotFound, discord.HTTPException):
log.exception("Unable to remove reaction")
# try: # try:
# await member.add_roles(*roles) # await member.add_roles(*roles)

Loading…
Cancel
Save