Skip to content
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

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions futaba/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
str_builder,
unicode,
utils,
gist,
)

__version__ = "0.6.2"
42 changes: 40 additions & 2 deletions futaba/cogs/moderation/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import discord
from discord.ext import commands

from futaba import permissions
from futaba.converters import MemberConv, UserConv
from futaba import permissions, gist
from futaba.converters import MemberConv, UserConv, MessageConv
from futaba.enums import PunishAction
from futaba.exceptions import CommandFailed, ManualCheckFailure
from futaba.navi import PunishTask
Expand Down Expand Up @@ -439,3 +439,41 @@ async def unban(self, ctx, user: UserConv, *, reason: str):
raise ManualCheckFailure(
content="I don't have permission to unban this user"
)

@commands.command(name="msgcollapse", aliases=["gist"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a thought: we could have !gist create a gist of arbitrary messages kind of like !msg does, and then have !mvgist or something that does what this currently performs. Then you have a generic helper method do the actual gist-ing. This can be a separate PR ofc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, since I was splitting the command into a helper function, it might as well be its own command.

@commands.guild_only()
async def msgcollapse(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 self.bot.config.gist_oauth_token:
raise CommandFailed(content="The gist oauth token is not configured.")

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")

logger.info("Collapsing %d messages into a gist", len(messages))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the guild ID and the user ID requesting it, and possibly the message IDs that were requested as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


gist_url = await gist.create_single_gist(
token=self.bot.config.gist_oauth_token,
content=" \n".join(str(message.content) for message in messages),
filename="collapsed.md",
description="Discord collapsed messages",
public=False,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, I would recommend a generic method on bot that fills in the arguments that are common

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So should I add these & token into settings(cog & sql)? Or maybe move the whole functionality into like an optional cog so that it's distinct from moderation, and would use the optional_cog_settings table?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be in the main cog I think, I'm just suggesting you have a helper method for creating gists. This way it's easier for other parts of the bot in the future to also use it without violating DRY.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I just thought that there might be an issue with storing the github token in the main config because the bot might be used by multiple guilds.

Copy link
Contributor Author

@tser0f tser0f Jul 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it okay that the helper method is also a command? Because otherwise it would just be a passthrough method. I also changed the in-code string literals to configuration options.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

futaba is only in 3 guilds, however having the allowing a github token per guild would be fine, lets the guild manage the gists created, a help method was just suggested in case we want other thing to be added to a gist that way it can be managed in one place


embed = discord.Embed(description="Done! Messages successfully collapsed!")
embed.add_field(name="Permalink", value=gist_url)
embed.colour = discord.Colour.dark_teal()

for message in messages:
await message.delete()

await ctx.send(embed=embed)
7 changes: 6 additions & 1 deletion futaba/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ def wrapper(value):
"error-channel-id": Or(And(str, ID_REGEX.match), "0"),
},
"cogs": {"example": object, "statbot": object},
"moderation": {"max-cleanup-messages": And(str, _check_gtz(int))},
"moderation": {
"max-cleanup-messages": And(str, _check_gtz(int)),
"gist-oauth-token": str,
},
"delay": {
"chunk-size": And(str, _check_gtz(int)),
"sleep": And(str, _check_gtz(float)),
Expand All @@ -68,6 +71,7 @@ def wrapper(value):
"error_channel_id",
"optional_cogs",
"max_cleanup_messages",
"gist_oauth_token",
"delay_chunk_size",
"delay_sleep",
"anger_emoji_id",
Expand All @@ -92,6 +96,7 @@ def load_config(path):
error_channel_id=int(config["bot"]["error-channel-id"]),
optional_cogs=config["cogs"],
max_cleanup_messages=int(config["moderation"]["max-cleanup-messages"]),
gist_oauth_token=config["moderation"]["gist-oauth-token"],
delay_chunk_size=int(config["delay"]["chunk-size"]),
delay_sleep=float(config["delay"]["sleep"]),
anger_emoji_id=int(config["emojis"]["anger"]),
Expand Down
49 changes: 49 additions & 0 deletions futaba/gist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# 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")


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")
4 changes: 4 additions & 0 deletions misc/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ url = "postgres://statbot:passwordhere@localhost/statbot_ro"
# when doing a bulk message cleanup
max-cleanup-messages = "500"

# Your github oauth token for creating gists with the
# !gist command.
gist-oauth-token = ""

[delay]
# Configuration for the delayed event queue

Expand Down