-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Collapse long messages into gist command #362
Changes from all commits
25e4069
76bf201
5dab69e
d8f3ec9
408d6b8
7da5791
d205943
aa2ab41
bd275a3
9e03516
8007f9b
54bb0ae
78411d2
ad00df6
4a21268
e325735
d12b2a7
306fae2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# | ||
# cogs/optional/gist/__init__.py | ||
# | ||
# futaba - A Discord Mod bot for the Programming server | ||
# Copyright (c) 2017-2020 Jake Richardson, Ammon Smith, jackylam5 | ||
# | ||
# futaba is available free of charge under the terms of the MIT | ||
# License. You are free to redistribute and/or modify it under those | ||
# terms. It is distributed in the hopes that it will be useful, but | ||
# WITHOUT ANY WARRANTY. See the LICENSE file for more details. | ||
# | ||
|
||
from .core import Gist | ||
|
||
# Setup for when cog is loaded | ||
def setup(bot): | ||
setup_gist(bot) | ||
|
||
|
||
def setup_gist(bot): | ||
cog = Gist(bot) | ||
bot.add_cog(cog) | ||
|
||
|
||
# Remove all the cogs when cog is unloaded | ||
def teardown(bot): | ||
teardown_gist(bot) | ||
|
||
|
||
def teardown_gist(bot): | ||
bot.remove_cog(Gist.__name__) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
# | ||
# cogs/optional/gist/core.py | ||
# | ||
# futaba - A Discord Mod bot for the Programming server | ||
# Copyright (c) 2017-2020 Jake Richardson, Ammon Smith, jackylam5 | ||
# | ||
# futaba is available free of charge under the terms of the MIT | ||
# License. You are free to redistribute and/or modify it under those | ||
# terms. It is distributed in the hopes that it will be useful, but | ||
# WITHOUT ANY WARRANTY. See the LICENSE file for more details. | ||
# | ||
|
||
""" | ||
Cog for creating gists from messages | ||
""" | ||
|
||
# REMOVE THIS IN REGULAR COGS: | ||
# pylint: disable=unused-import | ||
|
||
import asyncio | ||
import logging | ||
import math | ||
|
||
import discord | ||
from discord.ext import commands | ||
|
||
from futaba import permissions | ||
from futaba.cogs.abc import AbstractCog | ||
from futaba.converters import MessageConv | ||
from futaba.exceptions import CommandFailed, ManualCheckFailure, SendHelp | ||
from futaba.permissions import mod_perm | ||
from futaba.utils import user_discrim | ||
|
||
from .gist import create_single_gist | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class Gist(AbstractCog): | ||
__slots__ = ("journal",) | ||
|
||
def __init__(self, bot): | ||
super().__init__(bot) | ||
self.journal = bot.get_broadcaster("/gist") | ||
self.default_settings = { | ||
"token": "", | ||
"description": "Messages uploaded by futaba", | ||
"filename": "messages.md", | ||
"public": False, | ||
} | ||
|
||
def setup(self): | ||
# Fetching information from the database for this cog | ||
pass | ||
|
||
def get_settings(self, guild): | ||
return self.bot.sql.settings.get_optional_cog_settings(guild, "gist") | ||
|
||
def set_settings(self, guild, settings): | ||
return self.bot.sql.settings.set_optional_cog_settings(guild, "gist", settings) | ||
|
||
def change_setting(self, guild, setting, value): | ||
settings = self.get_settings(guild) | ||
|
||
if len(settings) == 0: | ||
settings = self.default_settings | ||
|
||
settings[setting] = value | ||
|
||
self.set_settings(guild, settings) | ||
|
||
@staticmethod | ||
def format_message_contents(message): | ||
contents = message.content.split("\n") | ||
contents = " \n".join(">" + content for content in contents) | ||
|
||
return f"{user_discrim(message.author)}: \n{contents} \n \n" | ||
|
||
@commands.command(name="gist", aliases=["msgupload"]) | ||
@commands.guild_only() | ||
async def upload_message(self, ctx, *messages: MessageConv): | ||
""" | ||
Concatenates the range of messages and upload to a gist. | ||
A link to the gist is posted after a successful upload. | ||
""" | ||
if len(messages) == 0: | ||
raise CommandFailed( | ||
content="Please specify the messages that should be uploaded." | ||
) | ||
|
||
settings = self.get_settings(ctx.guild) | ||
oauth_token = settings.get("token") | ||
|
||
if not oauth_token: | ||
raise CommandFailed(content="The gist oauth token is not configured.") | ||
|
||
messages_content = "\n".join( | ||
self.format_message_contents(message) for message in messages | ||
) | ||
messages_ids = ", ".join(str(message.id) for message in messages) | ||
|
||
gist_url = await create_single_gist( | ||
token=oauth_token, | ||
content=messages_content, | ||
filename=settings.get("filename"), | ||
description=settings.get("description"), | ||
public=settings.get("public"), | ||
) | ||
|
||
logger.info( | ||
"Successfully uploaded %d messages[%s] into a gist. Requested by user '%s' (id=%d, guild=%d)", | ||
len(messages), | ||
messages_ids, | ||
ctx.author.name, | ||
ctx.author.id, | ||
ctx.guild.id, | ||
) | ||
|
||
embed = discord.Embed(description="Done! Messages successfully uploaded!") | ||
embed.add_field(name="Permalink", value=gist_url) | ||
embed.colour = discord.Colour.dark_teal() | ||
|
||
await ctx.send(embed=embed) | ||
|
||
@commands.command(name="mvgist", aliases=["msgcollapse"]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also add the full form |
||
@commands.guild_only() | ||
async def collapse_message(self, ctx, *messages: MessageConv): | ||
""" | ||
Concatenates the range of messages and uploads to a gist. | ||
The original messages are deleted and a link to the gist is posted. | ||
|
||
Note: The messages specified should be by the same user | ||
""" | ||
|
||
if not permissions.has_perm(ctx, "manage_messages") and any( | ||
message.author.id != ctx.author.id for message in messages | ||
): | ||
# check if the messages were created by the same user | ||
raise ManualCheckFailure(content="I can only collapse your messages") | ||
|
||
await self.upload_message(ctx, *messages) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would prefer if this was a common helper method instead of directly invoking another command. If arguments or things involving pre/post command hooks change then this will cause issues. |
||
|
||
for message in messages: | ||
await message.delete() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Parallelize this with |
||
|
||
logger.info( | ||
"Removed %d messages because of message collapse request by user '%s'(id=%d, guild=%d)", | ||
len(messages), | ||
ctx.author.name, | ||
ctx.author.id, | ||
ctx.guild.id, | ||
) | ||
|
||
@commands.group(name="gistconf") | ||
@commands.guild_only() | ||
async def gist_settings(self, ctx): | ||
""" Manages settings related to gists """ | ||
|
||
if ctx.invoked_subcommand is None: | ||
raise SendHelp() | ||
|
||
@gist_settings.command(name="get") | ||
@commands.guild_only() | ||
@permissions.check_admin() | ||
async def settings_get(self, ctx, setting: str = None): | ||
""" | ||
Gets the current settings | ||
Optionally a setting name can be specified | ||
""" | ||
|
||
settings = self.get_settings(ctx.guild) | ||
|
||
embed = discord.Embed( | ||
description="Gist cog settings", colour=discord.Colour.dark_teal() | ||
) | ||
|
||
for key, val in settings.items(): | ||
if setting is None or setting == key: | ||
embed.add_field(name=key, value=val) | ||
|
||
await ctx.send(embed=embed) | ||
|
||
@gist_settings.command(name="token") | ||
@commands.guild_only() | ||
@permissions.check_admin() | ||
async def settings_token(self, ctx, value: str = None): | ||
""" | ||
Gets the currently set github token | ||
If you are an administrator you can change this value | ||
""" | ||
|
||
if value is not None: | ||
self.change_setting(ctx.guild, "token", value) | ||
|
||
await self.settings_get(ctx, "token") | ||
|
||
@gist_settings.command(name="description") | ||
@commands.guild_only() | ||
async def settings_description(self, ctx, value: str = None): | ||
""" | ||
Gets the currently set gist description | ||
If you are a moderator you can change this value | ||
""" | ||
|
||
if value is not None: | ||
if mod_perm(ctx): | ||
self.change_setting(ctx.guild, "description", value) | ||
else: | ||
raise ManualCheckFailure( | ||
content="You do not have persmissions to change the gist description" | ||
) | ||
|
||
await self.settings_get(ctx, "description") | ||
|
||
@gist_settings.command(name="filename") | ||
@commands.guild_only() | ||
async def settings_filename(self, ctx, value: str = None): | ||
""" | ||
Gets the currently set gist filename | ||
If you are a moderator you can change this value | ||
""" | ||
|
||
if value is not None: | ||
if mod_perm(ctx): | ||
self.change_setting(ctx.guild, "filename", value) | ||
else: | ||
raise ManualCheckFailure( | ||
content="You do not have persmissions to change the gist filename" | ||
) | ||
|
||
await self.settings_get(ctx, "filename") | ||
|
||
@gist_settings.command(name="public") | ||
@commands.guild_only() | ||
async def settings_public(self, ctx, value: bool = None): | ||
""" | ||
Gets whether gists are public and available for anyone to see | ||
If you are a moderator you can change this value | ||
""" | ||
|
||
if value is not None: | ||
if mod_perm(ctx): | ||
self.change_setting(ctx.guild, "public", value) | ||
else: | ||
raise ManualCheckFailure( | ||
content="You do not have persmissions to change whether uploaded gists are public" | ||
) | ||
|
||
await self.settings_get(ctx, "public") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# | ||
# cogs/optional/gist/gist.py | ||
# | ||
# futaba - A Discord Mod bot for the Programming server | ||
# Copyright (c) 2017-2020 Jake Richardson, Ammon Smith, jackylam5 | ||
# | ||
# futaba is available free of charge under the terms of the MIT | ||
# License. You are free to redistribute and/or modify it under those | ||
# terms. It is distributed in the hopes that it will be useful, but | ||
# WITHOUT ANY WARRANTY. See the LICENSE file for more details. | ||
# | ||
|
||
import logging | ||
import urllib.parse | ||
|
||
import aiohttp | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
__all__ = ["create_single_gist"] | ||
|
||
github_api_url = "https://api.github.com/" | ||
github_gist_endpoint = urllib.parse.urljoin(github_api_url, "/gists") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. constants should be |
||
|
||
|
||
async def create_single_gist( | ||
token, content, filename="message.md", description="", public: bool = True | ||
) -> str: | ||
""" | ||
Creates a new gist as specified by the parameters | ||
Returns the url that the new gist can be accessed by | ||
""" | ||
|
||
github_headers = { | ||
"Accept": "application/vnd.github.v3+json", | ||
"Authorization": f"token {token}", | ||
} | ||
request_data = { | ||
"description": description, | ||
"public": public, | ||
"files": {filename: {"content": content}}, | ||
} | ||
|
||
async with aiohttp.ClientSession( | ||
headers=github_headers, raise_for_status=True | ||
) as session: | ||
async with session.post(github_gist_endpoint, json=request_data) as resp: | ||
response_object = await resp.json() | ||
return response_object.get("html_url") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bot shouldn't be this excited, I would limit it to a single exclamation point