[V3 Everything] Package bot and write setup scripts (#964)

Ya'll are gonna hate me.

* Initial modifications

* Add initial setup.py

* working setup py help

* Modify setup file to package stuff

* Move a bunch of shit and fix imports

* Fix or skip tests

* Must add init files for find_packages to work

* Move main to scripts folder and rename

* Add shebangs

* Copy over translation files

* WORKING PIP INSTALL

* add dependency information

* Hardcoded version for now, will need to figure out a better way to do this

* OKAY ITS FINALLY FUCKING WORKING

* Add this guy

* Fix stuff

* Change readme to rst

* Remove double sentry opt in

* Oopsie

* Fix this thing

* Aaaand fix test

* Aaaand fix test

* Fix core cog importing and default cog install path

* Adjust readme

* change instance name from optional to required

* Ayyy let's do more dependency injection
This commit is contained in:
Will
2017-09-08 23:14:32 -04:00
committed by GitHub
parent 6b1fc786ee
commit d69fd63da7
85 changed files with 451 additions and 255 deletions

View File

@@ -0,0 +1,5 @@
from .general import General
def setup(bot):
bot.add_cog(General())

View File

@@ -0,0 +1,340 @@
import datetime
import time
from enum import Enum
from random import randint, choice
from urllib.parse import quote_plus
import aiohttp
import discord
from redbot.core.i18n import CogI18n
from discord.ext import commands
from redbot.core.utils.chat_formatting import escape, italics, pagify
_ = CogI18n("General", __file__)
class RPS(Enum):
rock = "\N{MOYAI}"
paper = "\N{PAGE FACING UP}"
scissors = "\N{BLACK SCISSORS}"
class RPSParser:
def __init__(self, argument):
argument = argument.lower()
if argument == "rock":
self.choice = RPS.rock
elif argument == "paper":
self.choice = RPS.paper
elif argument == "scissors":
self.choice = RPS.scissors
else:
raise
class General:
"""General commands."""
def __init__(self):
self.stopwatches = {}
self.ball = [
_("As I see it, yes"), _("It is certain"), _("It is decidedly so"),
_("Most likely"), _("Outlook good"), _("Signs point to yes"),
_("Without a doubt"), _("Yes"), _("Yes definitely"), _("You may rely on it"),
_("Reply hazy, try again"), _("Ask again later"),
_("Better not tell you now"), _("Cannot predict now"),
_("Concentrate and ask again"), _("Don't count on it"), _("My reply is no"),
_("My sources say no"), _("Outlook not so good"), _("Very doubtful")
]
@commands.command(hidden=True)
async def ping(self, ctx):
"""Pong."""
await ctx.send("Pong.")
@commands.command()
async def choose(self, ctx, *choices):
"""Chooses between multiple choices.
To denote multiple choices, you should use double quotes.
"""
choices = [escape(c, mass_mentions=True) for c in choices]
if len(choices) < 2:
await ctx.send(_('Not enough choices to pick from.'))
else:
await ctx.send(choice(choices))
@commands.command()
async def roll(self, ctx, number : int = 100):
"""Rolls random number (between 1 and user choice)
Defaults to 100.
"""
author = ctx.author
if number > 1:
n = randint(1, number)
await ctx.send(
_("{} :game_die: {} :game_die:").format(author.mention, n)
)
else:
await ctx.send(_("{} Maybe higher than 1? ;P").format(author.mention))
@commands.command()
async def flip(self, ctx, user: discord.Member=None):
"""Flips a coin... or a user.
Defaults to coin.
"""
if user != None:
msg = ""
if user.id == ctx.bot.user.id:
user = ctx.author
msg = _("Nice try. You think this is funny?"
"How about *this* instead:\n\n")
char = "abcdefghijklmnopqrstuvwxyz"
tran = "ɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz"
table = str.maketrans(char, tran)
name = user.display_name.translate(table)
char = char.upper()
tran = "∀qƆpƎℲפHIſʞ˥WNOԀQᴚS┴∩ΛMX⅄Z"
table = str.maketrans(char, tran)
name = name.translate(table)
await ctx.send(msg + "(╯°□°)╯︵ " + name[::-1])
else:
await ctx.send(
_("*flips a coin and... ") + choice([_("HEADS!*"), _("TAILS!*")])
)
@commands.command()
async def rps(self, ctx, your_choice : RPSParser):
"""Play rock paper scissors"""
author = ctx.author
player_choice = your_choice.choice
red_choice = choice((RPS.rock, RPS.paper, RPS.scissors))
cond = {
(RPS.rock, RPS.paper) : False,
(RPS.rock, RPS.scissors) : True,
(RPS.paper, RPS.rock) : True,
(RPS.paper, RPS.scissors) : False,
(RPS.scissors, RPS.rock) : False,
(RPS.scissors, RPS.paper) : True
}
if red_choice == player_choice:
outcome = None # Tie
else:
outcome = cond[(player_choice, red_choice)]
if outcome is True:
await ctx.send(_("{} You win {}!").format(
red_choice.value, author.mention
))
elif outcome is False:
await ctx.send(_("{} You lose {}!").format(
red_choice.value, author.mention
))
else:
await ctx.send(_("{} We're square {}!").format(
red_choice.value, author.mention
))
@commands.command(name="8", aliases=["8ball"])
async def _8ball(self, ctx, *, question : str):
"""Ask 8 ball a question
Question must end with a question mark.
"""
if question.endswith("?") and question != "?":
await ctx.send("`" + choice(self.ball) + "`")
else:
await ctx.send(_("That doesn't look like a question."))
@commands.command(aliases=["sw"])
async def stopwatch(self, ctx):
"""Starts/stops stopwatch"""
author = ctx.author
if not author.id in self.stopwatches:
self.stopwatches[author.id] = int(time.perf_counter())
await ctx.send(author.mention + _(" Stopwatch started!"))
else:
tmp = abs(self.stopwatches[author.id] - int(time.perf_counter()))
tmp = str(datetime.timedelta(seconds=tmp))
await ctx.send(author.mention + _(" Stopwatch stopped! Time: **") + tmp + "**")
self.stopwatches.pop(author.id, None)
@commands.command()
async def lmgtfy(self, ctx, *, search_terms : str):
"""Creates a lmgtfy link"""
search_terms = escape(search_terms.replace(" ", "+"), mass_mentions=True)
await ctx.send("https://lmgtfy.com/?q={}".format(search_terms))
@commands.command(hidden=True)
@commands.guild_only()
async def hug(self, ctx, user : discord.Member, intensity : int=1):
"""Because everyone likes hugs
Up to 10 intensity levels."""
name = italics(user.display_name)
if intensity <= 0:
msg = "(っ˘̩╭╮˘̩)っ" + name
elif intensity <= 3:
msg = "(っ´▽`)っ" + name
elif intensity <= 6:
msg = "╰(*´︶`*)╯" + name
elif intensity <= 9:
msg = "(つ≧▽≦)つ" + name
elif intensity >= 10:
msg = "(づ ̄ ³ ̄)づ{} ⊂(´・ω・`⊂)".format(name)
await ctx.send(msg)
@commands.command()
@commands.guild_only()
async def userinfo(self, ctx, *, user: discord.Member=None):
"""Shows users's informations"""
author = ctx.author
guild = ctx.guild
if not user:
user = author
# A special case for a special someone :^)
special_date = datetime.datetime(2016, 1, 10, 6, 8, 4, 443000)
is_special = (user.id == 96130341705637888 and
guild.id == 133049272517001216)
roles = sorted(user.roles)[1:]
joined_at = user.joined_at if not is_special else special_date
since_created = (ctx.message.created_at - user.created_at).days
since_joined = (ctx.message.created_at - joined_at).days
user_joined = joined_at.strftime("%d %b %Y %H:%M")
user_created = user.created_at.strftime("%d %b %Y %H:%M")
member_number = sorted(guild.members,
key=lambda m: m.joined_at).index(user) + 1
created_on = _("{}\n({} days ago)").format(user_created, since_created)
joined_on = _("{}\n({} days ago)").format(user_joined, since_joined)
game = _("Chilling in {} status").format(user.status)
if user.game and user.game.name and user.game.url:
game = _("Streaming: [{}]({})").format(user.game, user.game.url)
elif user.game and user.game.name:
game = _("Playing {}").format(user.game)
if roles:
roles = ", ".join([x.name for x in roles])
else:
roles = _("None")
data = discord.Embed(description=game, colour=user.colour)
data.add_field(name=_("Joined Discord on"), value=created_on)
data.add_field(name=_("Joined this guild on"), value=joined_on)
data.add_field(name=_("Roles"), value=roles, inline=False)
data.set_footer(text=_("Member #{} | User ID: {}"
"").format(member_number, user.id))
name = str(user)
name = " ~ ".join((name, user.nick)) if user.nick else name
if user.avatar:
data.set_author(name=name, url=user.avatar_url)
data.set_thumbnail(url=user.avatar_url)
else:
data.set_author(name=name)
try:
await ctx.send(embed=data)
except discord.HTTPException:
await ctx.send(_("I need the `Embed links` permission "
"to send this."))
@commands.command()
@commands.guild_only()
async def serverinfo(self, ctx):
"""Shows guild's informations"""
guild = ctx.guild
online = len([m.status for m in guild.members
if m.status == discord.Status.online or
m.status == discord.Status.idle])
total_users = len(guild.members)
text_channels = len(guild.text_channels)
voice_channels = len(guild.voice_channels)
passed = (ctx.message.created_at - guild.created_at).days
created_at = (_("Since {}. That's over {} days ago!"
"").format(guild.created_at.strftime("%d %b %Y %H:%M"),
passed))
colour = ''.join([choice('0123456789ABCDEF') for x in range(6)])
colour = randint(0, 0xFFFFFF)
data = discord.Embed(
description=created_at,
colour=discord.Colour(value=colour))
data.add_field(name=_("Region"), value=str(guild.region))
data.add_field(name=_("Users"), value="{}/{}".format(online, total_users))
data.add_field(name=_("Text Channels"), value=text_channels)
data.add_field(name=_("Voice Channels"), value=voice_channels)
data.add_field(name=_("Roles"), value=len(guild.roles))
data.add_field(name=_("Owner"), value=str(guild.owner))
data.set_footer(text=_("Guild ID: ") + str(guild.id))
if guild.icon_url:
data.set_author(name=guild.name, url=guild.icon_url)
data.set_thumbnail(url=guild.icon_url)
else:
data.set_author(name=guild.name)
try:
await ctx.send(embed=data)
except discord.HTTPException:
await ctx.send(_("I need the `Embed links` permission "
"to send this."))
@commands.command()
async def urban(self, ctx, *, search_terms: str, definition_number: int=1):
"""Urban Dictionary search
Definition number must be between 1 and 10"""
def encode(s):
return quote_plus(s, encoding='utf-8', errors='replace')
# definition_number is just there to show up in the help
# all this mess is to avoid forcing double quotes on the user
search_terms = search_terms.split(" ")
try:
if len(search_terms) > 1:
pos = int(search_terms[-1]) - 1
search_terms = search_terms[:-1]
else:
pos = 0
if pos not in range(0, 11): # API only provides the
pos = 0 # top 10 definitions
except ValueError:
pos = 0
search_terms = {"term": "+".join([s for s in search_terms])}
url = "http://api.urbandictionary.com/v0/define"
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, params=search_terms) as r:
result = await r.json()
item_list = result["list"]
if item_list:
definition = item_list[pos]['definition']
example = item_list[pos]['example']
defs = len(item_list)
msg = ("**Definition #{} out of {}:\n**{}\n\n"
"**Example:\n**{}".format(pos+1, defs, definition,
example))
msg = pagify(msg, ["\n"])
for page in msg:
await ctx.send(page)
else:
await ctx.send(_("Your search terms gave no results."))
except IndexError:
await ctx.send(_("There is no definition #{}").format(pos+1))
except:
await ctx.send(_("Error."))

View File

@@ -0,0 +1,237 @@
# Copyright (C) 2017 Red-DiscordBot
# UltimatePancake <pier.gaetani@gmail.com>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2017-08-26 17:50+EDT\n"
"PO-Revision-Date: 2017-08-26 20:26-0600\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 2.0.3\n"
"Last-Translator: UltimatePancake <pier.gaetani@gmail.com>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: es\n"
#: ../general.py:40
msgid "As I see it, yes"
msgstr "Como lo veo, si"
#: ../general.py:40
msgid "It is certain"
msgstr "Es cierto"
#: ../general.py:40
msgid "It is decidedly so"
msgstr "Decididamente"
#: ../general.py:41
msgid "Most likely"
msgstr "Probablemente"
#: ../general.py:41
msgid "Outlook good"
msgstr "El panorama se ve bien"
#: ../general.py:41
msgid "Signs point to yes"
msgstr "Todo apunta a sí"
#: ../general.py:42
msgid "Without a doubt"
msgstr "Sin duda alguna"
#: ../general.py:42
msgid "Yes"
msgstr "Sí"
#: ../general.py:42
msgid "Yes definitely"
msgstr "Sí definitivamente"
#: ../general.py:42
msgid "You may rely on it"
msgstr "Puedes contar con ello"
#: ../general.py:43
msgid "Ask again later"
msgstr "Pregunta más tarde"
#: ../general.py:43
msgid "Reply hazy, try again"
msgstr "Respuesta borrosa, intenta de nuevo"
#: ../general.py:44
msgid "Better not tell you now"
msgstr "Mejor no te digo en este momento"
#: ../general.py:44
msgid "Cannot predict now"
msgstr "No puedo predecir en este momento"
#: ../general.py:45
msgid "Concentrate and ask again"
msgstr "Concéntrate y pregunta de nuevo"
#: ../general.py:45
msgid "Don't count on it"
msgstr "No cuentes con ello"
#: ../general.py:45
msgid "My reply is no"
msgstr "Mi respuesta es no"
#: ../general.py:46
msgid "My sources say no"
msgstr "Mis fuentes dicen no"
#: ../general.py:46
msgid "Outlook not so good"
msgstr "El panorama no se ve bien"
#: ../general.py:46
msgid "Very doubtful"
msgstr "Lo dudo mucho"
#: ../general.py:62
msgid "Not enough choices to pick from."
msgstr "Insuficientes opciones para elegir"
#: ../general.py:76
msgid "{} :game_die: {} :game_die:"
msgstr "{} :game_die: {} :game_die:"
#: ../general.py:79
msgid "{} ¿Maybe higher than 1? ;P"
msgstr "{} ¿Tal vez más que 1? ;P"
#: ../general.py:91
msgid ""
"Nice try. You think this is funny?How about *this* instead:\n"
"\n"
msgstr ""
"Buen intento. ¿Te parece gracioso? Qué tal *esto* mejor:\n"
"\n"
#: ../general.py:104
msgid "*flips a coin and... "
msgstr "*tira una moneda y..."
#: ../general.py:104
msgid "HEADS!*"
msgstr "¡CARA!*"
#: ../general.py:104
msgid "TAILS!*"
msgstr "¡CRUZ!*"
#: ../general.py:128
msgid "{} You win {}!"
msgstr "{} ¡Ganas {}!"
#: ../general.py:132
msgid "{} You lose {}!"
msgstr "{} ¡Pierdes {}!"
#: ../general.py:136
msgid "{} We're square {}!"
msgstr "{} ¡Empates {}!"
#: ../general.py:149
msgid "That doesn't look like a question."
msgstr "Eso no parece pregunta."
#: ../general.py:157
msgid " Stopwatch started!"
msgstr " ¡Cronómetro comenzado!"
#: ../general.py:161
msgid " Stopwatch stopped! Time: **"
msgstr " ¡Cronómetro detenido! Tiempo: **"
#: ../general.py:214 ../general.py:215
msgid ""
"{}\n"
"({} days ago)"
msgstr ""
"{}\n"
"(Hace {} días)"
#: ../general.py:217
msgid "Chilling in {} status"
msgstr "Ahí no mas en estado {}"
#: ../general.py:220
msgid "Streaming: [{}]({})"
msgstr "Transmitiendo: [{}]({})"
#: ../general.py:222
msgid "Playing {}"
msgstr "Jugando {}"
#: ../general.py:227
msgid "None"
msgstr "Nada"
#: ../general.py:230
msgid "Joined Discord on"
msgstr "Registrado a Discord en"
#: ../general.py:231
msgid "Joined this guild on"
msgstr "Registrado a gremio en"
#: ../general.py:232 ../general.py:277
msgid "Roles"
msgstr "Roles"
#: ../general.py:233
msgid "Member #{} | User ID: {}"
msgstr "Miembro #{} | ID de usuario: {}"
#: ../general.py:248 ../general.py:290
msgid "I need the `Embed links` permission to send this."
msgstr "Necesito el permiso `Insertar Enlaces` para enviar esto."
#: ../general.py:263
msgid "Since {}. That's over {} days ago!"
msgstr "Desde {}. Hace {} días!"
#: ../general.py:273
msgid "Region"
msgstr "Región"
#: ../general.py:274
msgid "Users"
msgstr "Usuarios"
#: ../general.py:275
msgid "Text Channels"
msgstr "Canales de texto"
#: ../general.py:276
msgid "Voice Channels"
msgstr "Canalez de voz"
#: ../general.py:278
msgid "Owner"
msgstr "Dueño"
#: ../general.py:279
msgid "Guild ID: "
msgstr "ID de gremio:"
#: ../general.py:334
msgid "Your search terms gave no results."
msgstr "Tu búsqueda no ha dado resultados."
#: ../general.py:336
msgid "There is no definition #{}"
msgstr "No existe la definición #{}"
#: ../general.py:338
msgid "Error."
msgstr "Error."

View File

@@ -0,0 +1,233 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2017-08-26 17:50+EDT\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
"Generated-By: pygettext.py 1.5\n"
#: ../general.py:40
msgid "As I see it, yes"
msgstr ""
#: ../general.py:40
msgid "It is certain"
msgstr ""
#: ../general.py:40
msgid "It is decidedly so"
msgstr ""
#: ../general.py:41
msgid "Most likely"
msgstr ""
#: ../general.py:41
msgid "Outlook good"
msgstr ""
#: ../general.py:41
msgid "Signs point to yes"
msgstr ""
#: ../general.py:42
msgid "Without a doubt"
msgstr ""
#: ../general.py:42
msgid "Yes"
msgstr ""
#: ../general.py:42
msgid "Yes definitely"
msgstr ""
#: ../general.py:42
msgid "You may rely on it"
msgstr ""
#: ../general.py:43
msgid "Ask again later"
msgstr ""
#: ../general.py:43
msgid "Reply hazy, try again"
msgstr ""
#: ../general.py:44
msgid "Better not tell you now"
msgstr ""
#: ../general.py:44
msgid "Cannot predict now"
msgstr ""
#: ../general.py:45
msgid "Concentrate and ask again"
msgstr ""
#: ../general.py:45
msgid "Don't count on it"
msgstr ""
#: ../general.py:45
msgid "My reply is no"
msgstr ""
#: ../general.py:46
msgid "My sources say no"
msgstr ""
#: ../general.py:46
msgid "Outlook not so good"
msgstr ""
#: ../general.py:46
msgid "Very doubtful"
msgstr ""
#: ../general.py:62
msgid "Not enough choices to pick from."
msgstr ""
#: ../general.py:76
msgid "{} :game_die: {} :game_die:"
msgstr ""
#: ../general.py:79
msgid "{} Maybe higher than 1? ;P"
msgstr ""
#: ../general.py:91
msgid ""
"Nice try. You think this is funny?How about *this* instead:\n"
"\n"
msgstr ""
#: ../general.py:104
msgid "*flips a coin and... "
msgstr ""
#: ../general.py:104
msgid "HEADS!*"
msgstr ""
#: ../general.py:104
msgid "TAILS!*"
msgstr ""
#: ../general.py:128
msgid "{} You win {}!"
msgstr ""
#: ../general.py:132
msgid "{} You lose {}!"
msgstr ""
#: ../general.py:136
msgid "{} We're square {}!"
msgstr ""
#: ../general.py:149
msgid "That doesn't look like a question."
msgstr ""
#: ../general.py:157
msgid " Stopwatch started!"
msgstr ""
#: ../general.py:161
msgid " Stopwatch stopped! Time: **"
msgstr ""
#: ../general.py:214 ../general.py:215
msgid ""
"{}\n"
"({} days ago)"
msgstr ""
#: ../general.py:217
msgid "Chilling in {} status"
msgstr ""
#: ../general.py:220
msgid "Streaming: [{}]({})"
msgstr ""
#: ../general.py:222
msgid "Playing {}"
msgstr ""
#: ../general.py:227
msgid "None"
msgstr ""
#: ../general.py:230
msgid "Joined Discord on"
msgstr ""
#: ../general.py:231
msgid "Joined this guild on"
msgstr ""
#: ../general.py:232 ../general.py:277
msgid "Roles"
msgstr ""
#: ../general.py:233
msgid "Member #{} | User ID: {}"
msgstr ""
#: ../general.py:248 ../general.py:290
msgid "I need the `Embed links` permission to send this."
msgstr ""
#: ../general.py:263
msgid "Since {}. That's over {} days ago!"
msgstr ""
#: ../general.py:273
msgid "Region"
msgstr ""
#: ../general.py:274
msgid "Users"
msgstr ""
#: ../general.py:275
msgid "Text Channels"
msgstr ""
#: ../general.py:276
msgid "Voice Channels"
msgstr ""
#: ../general.py:278
msgid "Owner"
msgstr ""
#: ../general.py:279
msgid "Guild ID: "
msgstr ""
#: ../general.py:334
msgid "Your search terms gave no results."
msgstr ""
#: ../general.py:336
msgid "There is no definition #{}"
msgstr ""
#: ../general.py:338
msgid "Error."
msgstr ""