ryuko-ng/cogs/pin.py
Ave Ozkal fd845ecb40
Do a black pass and add black to contributing guidelines
FORK MAINTAINERS: I'm so, so sorry, but this was planned since forever.
If you need help integrating this, feel free to contact me.
2020-04-21 01:05:32 +03:00

162 lines
5.6 KiB
Python

import config
from discord.ext import commands
from discord.ext.commands import Cog
from discord.enums import MessageType
from discord import Embed
import aiohttp
import gidgethub.aiohttp
from helpers.checks import check_if_collaborator
from helpers.checks import check_if_pin_channel
class Pin(Cog):
"""
Allow users to pin things
"""
def __init__(self, bot):
self.bot = bot
def is_pinboard(self, msg):
return (
msg.author == self.bot.user
and len(msg.embeds) > 0
and msg.embeds[0].title == "Pinboard"
)
async def get_pinboard(self, gh, channel):
# Find pinboard pin
pinboard_msg = None
for msg in reversed(await channel.pins()):
if self.is_pinboard(msg):
# Found pinboard, return content and gist id
id = msg.embeds[0].url.split("/")[-1]
data = await gh.getitem(f"/gists/{id}")
return (id, data["files"]["pinboard.md"]["content"])
# Create pinboard pin if it does not exist
data = await gh.post(
"/gists",
data={
"files": {
"pinboard.md": {"content": "Old pins are available here:\n\n"}
},
"description": f"Pinboard for SwitchRoot #{channel.name}",
"public": True,
},
)
msg = await channel.send(
embed=Embed(
title="Pinboard",
description="Old pins are moved to the pinboard to make space for \
new ones. Check it out!",
url=data["html_url"],
)
)
await msg.pin()
return (data["id"], data["files"]["pinboard.md"]["content"])
async def add_pin_to_pinboard(self, channel, data):
if config.github_oauth_token == "":
# Don't add to gist pinboard if we don't have an oauth token
return
async with aiohttp.ClientSession() as session:
gh = gidgethub.aiohttp.GitHubAPI(
session, "RoboCop-NG", oauth_token=config.github_oauth_token
)
(id, content) = await self.get_pinboard(gh, channel)
content += "- " + data + "\n"
await gh.patch(
f"/gists/{id}", data={"files": {"pinboard.md": {"content": content}}}
)
@commands.command()
@commands.guild_only()
@commands.check(check_if_collaborator)
@commands.check(check_if_pin_channel)
async def unpin(self, ctx, idx: int):
"""Unpins a pinned message."""
if idx <= 50:
# Get message by pin idx
target_msg = (await ctx.message.channel.pins())[idx]
else:
# Get message by ID
target_msg = await ctx.message.channel.get_message(idx)
if self.is_pinboard(target_msg):
await ctx.send("Cannot unpin pinboard!")
else:
await target_msg.unpin()
await target_msg.remove_reaction("📌", self.bot.user)
await ctx.send(f"Unpinned {target_msg.jump_url}")
# TODO: Remove from pinboard?
# Use raw_reaction to allow pinning old messages.
@Cog.listener()
async def on_raw_reaction_add(self, payload):
# Check that the user wants to pin this message
if payload.emoji.name not in ["📌", "📍"]:
return
# Check that reaction pinning is allowd in this channel
if payload.channel_id not in config.allowed_pin_channels:
return
target_guild = self.bot.get_guild(payload.guild_id)
if target_guild is None:
return
# Check that the user is allowed to reaction-pin
target_user = target_guild.get_member(payload.user_id)
for role in config.staff_role_ids + config.allowed_pin_roles:
if role in [role.id for role in target_user.roles]:
target_chan = self.bot.get_channel(payload.channel_id)
target_msg = await target_chan.get_message(payload.message_id)
# Check that the message hasn't already been pinned
for reaction in target_msg.reactions:
if reaction.emoji == "📌":
if reaction.me:
return
else:
break
# Add pin to pinboard, create one if none is found
await self.add_pin_to_pinboard(target_chan, target_msg.jump_url)
# Avoid staying "stuck" waiting for the pin message if message
# was already manually pinned
if not target_msg.pinned:
# If we already have 50 pins, we should unpin the oldest.
# We should avoid unpinning the pinboard.
pins = await target_chan.pins()
if len(pins) >= 50:
for msg in reversed(pins):
if not self.is_pinboard(msg):
await msg.unpin()
break
# Wait for the automated "Pinned" message so we can delete it
waitable = self.bot.wait_for("message", check=check)
# Pin the message
await target_msg.pin()
# Delete the automated Pinned message
msg = await waitable
await msg.delete()
# Add a Pin reaction so we remember that the message is pinned
await target_msg.add_reaction("📌")
def check(msg):
return msg.type is MessageType.pins_add
def setup(bot):
bot.add_cog(Pin(bot))