Merge branch 'master' into fifo_develop
This commit is contained in:
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",
|
||||||
|
5
firstmessage/__init__.py
Normal file
5
firstmessage/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .firstmessage import FirstMessage
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
bot.add_cog(FirstMessage(bot))
|
49
firstmessage/firstmessage.py
Normal file
49
firstmessage/firstmessage.py
Normal file
@ -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)
|
17
firstmessage/info.json
Normal file
17
firstmessage/info.json
Normal file
@ -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,9 +286,9 @@ 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"""
|
||||||
guild: discord.Guild = getattr(user, "guild", None)
|
guild: discord.Guild = getattr(user, "guild", None)
|
||||||
if guild is None:
|
if guild is None:
|
||||||
|
5
isitdown/__init__.py
Normal file
5
isitdown/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .isitdown import IsItDown
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
bot.add_cog(IsItDown(bot))
|
17
isitdown/info.json
Normal file
17
isitdown/info.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
58
isitdown/isitdown.py
Normal file
58
isitdown/isitdown.py
Normal file
@ -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…
x
Reference in New Issue
Block a user