Пример #1
0
        "usage":
        "!osu <option>\n"
        "Options:\n"
        "     set <username>\n"
        "     get\n"
        "     notify-channel [channel]\n",
        "desc":
        "Handle osu! commands.\n"
        "`set` assigns your osu! user for notifying.\n"
        "`get` returns an osu! userpage link..\n"
        "~~`notify-channel` sets a channel as the notify channel. This channel should not be used by any "
        "member. Specify no channel to disable. **Requires `Manage Server` permission.**~~"
    }
}

osu = Config("osu", data={"key": "change to your api key", "profiles": {}})
osu_tracking = {
}  # Saves the requested data or deletes whenever the user stops playing (for comparisons)
update_interval = 30  # Seconds
logging_interval = 30  # Minutes

request_limit = 100

osu_api = "https://osu.ppy.sh/api/"

logging.getLogger("requests").setLevel(logging.WARNING)


def calculate_acc(c50, c100, c300, miss):
    total_points_of_hits = int(c50) * 50 + int(c100) * 100 + int(c300) * 300
    total_number_of_hits = int(miss) + int(c50) + int(c100) + int(c300)
Пример #2
0
import discord

from pcbot import Config, Annotate, config, utils
import plugins

client = plugins.client  # type: discord.Client

alias_desc = \
    "Assign an alias command, where trigger is the command in it's entirety: `{pre}cmd` or `>cmd` or `cmd`.\n" \
    "Feel free to use spaces in a **trigger** by *enclosing it with quotes*, like so: `\"{pre}my cmd\"`.\n\n" \
    "**Options**:\n" \
    "`-anywhere` makes the alias trigger anywhere in a message, and not just the start of a message.\n" \
    "`-case-sensitive` ensures that you *need* to follow the same casing.\n" \
    "`-delete-message` removes the original message. This option can not be mixed with the `-anywhere` option.\n" \

aliases = Config("user_alias", data={})


@plugins.command(description=alias_desc, pos_check=lambda s: s.startswith("-"))
async def alias(message: discord.Message, *options: str.lower, trigger: str, text: Annotate.Content):
    """ Assign an alias. Description is defined in alias_desc. """
    anywhere = "-anywhere" in options
    case_sensitive = "-case-sensitive" in options
    delete_message = not anywhere and "-delete-message" in options

    if str(message.author.id) not in aliases.data:
        aliases.data[str(message.author.id)] = {}

    # Set options
    aliases.data[str(message.author.id)][trigger if case_sensitive else trigger.lower()] = dict(
        text=text,
Пример #3
0
try:
    from PIL import Image
except:
    resize = False
    logging.warning(
        "PIL could not be loaded. The pokedex works like usual, however sprites will remain 1x scaled."
    )
else:
    resize = True

client = plugins.client  # type: discord.Client

api_path = "plugins/pokedexlib/pokedex.json"
sprites_path = "plugins/pokedexlib/sprites/"
pokedex_config = Config("pokedex", data=defaultdict(dict))
default_scale_factor = 1.8
min_scale_factor, max_scale_factor = 0.25, 4

pokemon_go_gen = [1, 2, 3]

# Load the Pokedex API
with open(api_path) as api_file:
    api = json.load(api_file)
    pokedex = api["pokemon"]

# Load all our sprites into RAM (they don't take much space)
# Unlike the pokedex.json API, these use pokemon ID as keys.
# The values are the sprites in bytes.
sprites = {}
for file in os.listdir(sprites_path):
Пример #4
0
        "usage":
        "!twitch <option>\n"
        "Options:\n"
        "    set <username>\n"
        "    get\n"
        "    notify-channel [channel]",
        "desc":
        "Handle twitch commands.\n"
        "`set` assigns your twitch account for notifying.\n"
        "`get` returns a twitch channel link.\n"
        "~~`notify-channel` sets a channel as the notify channel. This channel should not be used by any "
        "member. Specify no channel to disable. **Requires `Manage Server` permission.**~~"
    }
}

twitch_channels = Config("twitch-channels", data={"channels": {}})
live_channels = {}
update_interval = 180  # Seconds

twitch_api = "https://api.twitch.tv/kraken"

logging.getLogger("requests").setLevel(logging.WARNING)


@asyncio.coroutine
def on_ready(client: discord.Client):
    while not client.is_closed:
        try:
            yield from asyncio.sleep(update_interval)

            # Go through all set channels (if they're online on discord) and update their status
Пример #5
0
Commands:
    roll
    feature
"""

import random
from re import match
from datetime import datetime, timedelta

import discord

from pcbot import utils, Config, Annotate
import plugins
client = plugins.client  # type: discord.Client

feature_reqs = Config(filename="feature_requests", data={})
# cleverbot = Cleverbot(config.name.replace(" ", "_") + "-discord-bot")


@plugins.command()
async def roll(message: discord.Message, num: utils.int_range(f=1) = 100):
    """ Roll a number from 1-100 if no second argument or second argument is not a number.
        Alternatively rolls `num` times (minimum 1). """
    rolled = random.randint(1, num)
    await client.say(message,
                     "{0.mention} rolls `{1}`.".format(message.author, rolled))


@plugins.command()
async def avatar(message: discord.Message,
                 member: Annotate.Member = Annotate.Self):
Пример #6
0
# Define some regexes for option checking in "summary" command
valid_num = re.compile(r"\*(?P<num>\d+)")
valid_member = utils.member_mention_pattern
valid_member_silent = re.compile(r"@\((?P<name>.+)\)")
valid_role = re.compile(r"<@&(?P<id>\d+)>")
valid_channel = utils.channel_mention_pattern
valid_options = ("+re", "+regex", "+case", "+tts", "+nobot", "+bot",
                 "+coherent", "+loose")

on_no_messages = "**There were no messages to generate a summary from, {0.author.name}.**"
on_fail = "**I was unable to construct a summary, {0.author.name}.**"

summary_options = Config("summary_options",
                         data=dict(no_bot=False,
                                   no_self=False,
                                   persistent_channels=[]),
                         pretty=True)
summary_data = Config("summary_data", data=dict(channels={}))


def to_persistent(message: discord.Message):
    return dict(content=message.clean_content,
                author=str(message.author.id),
                bot=message.author.bot)


async def update_messages(channel: discord.TextChannel):
    """ Download messages. """
    messages = stored_messages[str(channel.id)]  # type: deque
Пример #7
0
"""

import re
from collections import namedtuple, deque
from traceback import print_exc
from typing import Dict
from urllib import parse as url_parse

import asyncio
import discord

import plugins
from pcbot import utils, Annotate, Config
client = plugins.client  # type: discord.Client

music_channels = Config("music_channels", data=[])
voice_states = {}  # type: Dict[discord.Server, VoiceState]
youtube_dl_options = dict(format="bestaudio/best",
                          extractaudio=True,
                          audioformat="mp3",
                          noplaylist=True,
                          default_search="auto",
                          quiet=True,
                          nocheckcertificate=True)
ffmpeg_before_options = "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5"

max_songs_queued = 2  # How many songs each member are allowed in the queue at once
max_song_length = 60 * 120  # The maximum song length in seconds
default_volume = .6

if not discord.opus.is_loaded():
Пример #8
0
from plugins.osulib import api, Mods

client = plugins.client  # type: discord.Client

# Configuration data for this plugin, including settings for members and the API key
osu_config = Config(
    "osu",
    pretty=True,
    data=dict(
        key="change to your api key",
        pp_threshold=0.13,  # The amount of pp gain required to post a score
        score_request_limit=
        100,  # The maximum number of scores to request, between 0-100
        minimum_pp_required=
        0,  # The minimum pp required to assign a gamemode/profile in general
        use_mentions_in_scores=
        True,  # Whether the bot will mention people when they set a *score*
        profiles={},  # Profile setup as member_id: osu_id
        mode={},  # Member's game mode as member_id: gamemode_value
        server=
        {},  # Server specific info for score- and map notification channels
        update_mode=
        {},  # Member's notification update mode as member_id: UpdateModes.name
        primary_server=
        {},  # Member's primary server; defines where they should be mentioned: member_id: server_id
    ))

osu_tracking = {
}  # Saves the requested data or deletes whenever the user stops playing (for comparisons)
update_interval = 30  # The pause time in seconds between updates
time_elapsed = 0  # The registered time it takes to process all information between updates (changes each update)
Пример #9
0
command specific functions and helpers.
"""

import re
import shlex
from enum import Enum
from functools import wraps
from io import BytesIO

import aiohttp
import discord
from asyncio import subprocess as sub

from pcbot import Config, config

owner_cfg = Config("owner")
member_mention_regex = re.compile(r"<@!?(?P<id>\d+)>")
channel_mention_regex = re.compile(r"<#(?P<id>\d+)>")
markdown_code_regex = re.compile(
    r"^(?P<capt>`*)(?:[a-z]+\n)?(?P<code>.+)(?P=capt)$", flags=re.DOTALL)
identifier_prefix = re.compile(r"[a-zA-Z_]")

client = None  # Declare the Client. For python 3.6: client: discord.Client


def set_client(c: discord.Client):
    """ Assign the client to a variable. """
    global client
    client = c

Пример #10
0
""" Would you rather? This plugin includes would you rather functionality
"""
import asyncio
import random
import re

import discord

import plugins
from pcbot import Config

client = plugins.client  # type: discord.Client

db = Config("would-you-rather",
            data=dict(timeout=10,
                      responses=["**{name}** would **{choice}**!"],
                      questions=[]),
            pretty=True)
command_pattern = re.compile(r"(.+)(?:\s+or|\s*,)\s+([^?]+)\?*")
sessions = set()  # All running would you rather's are in this set


@plugins.argument(
    "{open}option ...{close} or/, {open}other option ...{close}[?]",
    allow_spaces=True)
async def options(arg):
    """ Command argument for receiving two options. """
    match = command_pattern.match(arg)
    assert match
    assert not match.group(1).lower() == match.group(
        2).lower(), "**The choices cannot be the same.**"
Пример #11
0
More to come?

Commands:
    twitch
"""

import discord
import logging
from datetime import datetime, timedelta

from pcbot import utils, Config
import plugins
from plugins.twitchlib import twitch
client = plugins.client  # type: discord.Client

twitch_config = Config("twitch-config", data=dict(guilds={}))

# Keep track of all {member.id: date} that are streaming
stream_history = {}
repeat_notification_delta = timedelta(hours=2)


async def on_reload(name):
    global stream_history
    local_history = stream_history

    await plugins.reload(name)

    stream_history = local_history

Пример #12
0
Файл: time.py Проект: stoz/PCBOT
import discord
import pendulum
from pytz import all_timezones

import plugins
from pcbot import Config, Annotate

client = plugins.client  # type: discord.Client

time_cfg = Config("time", data=dict(countdown={}, timezone={}))
dt_format = "%A, %d %B %Y %H:%M:%S"


@plugins.argument()
def tz_arg(timezone: str):
    """ Get timezone from a string. """
    for tz in all_timezones:
        if tz.lower().endswith(timezone.lower()):
            return tz
    return None


def reverse_gmt(timezone: str):
    """ POSIX is stupid so these are reversed. """
    if "+" in timezone:
        timezone = timezone.replace("+", "-")
    elif "-" in timezone:
        timezone = timezone.replace("-", "+")

    return timezone
Пример #13
0
import logging
import re
from collections import namedtuple

import discord

import plugins
from pcbot import Config

client = plugins.client  # type: discord.Client

blacklist = Config("blacklist",
                   data={
                       "enabled": False,
                       "global": {},
                       "server": [],
                       "channel": []
                   },
                   pretty=True)

blacklist_config_fieldnames = [
    "match_patterns", "regex_patterns", "case_sensitive", "response", "bots",
    "exclude", "words", "id", "override"
]
BlacklistConfig = namedtuple("BlacklistConfig",
                             " ".join(blacklist_config_fieldnames))
blacklist_cache = {}


def make_config_object(data: dict):
    """ Return a BlacklistConfig from the given dict. 
Пример #14
0
""" Plugin for compiling and executing brainfuck code. """
import asyncio

import discord

import plugins
from pcbot import Annotate, Config

client = plugins.client  # type: discord.Client

cfg = Config("brainfuck",
             data={})  # Keys are names and values are dict with author, code
max_iterations = 2**17
brainfuck_chars = "+-><][.,"


class Loop:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.pointer = None

    def set_pointer(self, pointer):
        self.pointer = (pointer.cursor, pointer.value)

    def compare_pointer(self, pointer):
        if self.pointer is None:
            return False

        return self.pointer == (pointer.cursor, pointer.value)
Пример #15
0
    unmute
    timeout
    suspend
"""

from collections import defaultdict

import discord
import asyncio

from pcbot import Config, utils, Annotate
import plugins
client = plugins.client  # type: discord.Client


moderate = Config("moderate", data=defaultdict(dict))
default_config = {}  # Used by add_setting helper function


def setup_default_config(server: discord.Server):
    """ Setup default settings for a server. """
    # Set to defaults if there is no config for the server
    if server.id not in moderate.data:
        moderate.data[server.id] = default_config
        moderate.save()
        return

    # Set to defaults if server's config is missing values
    if not all(k in moderate.data[server.id].keys() for k in default_config):
        moderate.data[server.id] = default_config
        moderate.save()
Пример #16
0
import importlib
import inspect
import logging
import random
from datetime import datetime, timedelta

import discord
import asyncio

from pcbot import utils, Config, Annotate, config
import plugins

client = plugins.client  # type: discord.Client

sub = asyncio.subprocess
lambdas = Config("lambdas", data={})
lambda_config = Config("lambda-config", data=dict(imports=[], blacklist=[]))

code_globals = {}


@plugins.command(name="help", aliases="commands")
async def help_(message: discord.Message, command: str.lower = None, *args):
    """ Display commands or their usage and description. """
    command_prefix = config.guild_command_prefix(message.guild)

    # Display the specific command
    if command:
        if command.startswith(command_prefix):
            command = command[len(command_prefix):]
Пример #17
0
logs_from_limit = 5000
max_summaries = 5
update_task = asyncio.Event()
update_task.set()

# Define some regexes for option checking in "summary" command
valid_num = re.compile(r"\*(?P<num>\d+)")
valid_member = utils.member_mention_regex
valid_member_silent = re.compile(r"@\((?P<name>.+)\)")
valid_channel = utils.channel_mention_regex
valid_options = ("+re", "+regex", "+case", "+tts", "+nobot", "+bot")

on_no_messages = "**There were no messages to generate a summary from, {0.author.name}.**"
on_fail = "**I was unable to construct a summary, {0.author.name}.**"

summary_options = Config("summary_options", data=dict(no_bot=False, no_self=False), pretty=True)


async def update_messages(channel: discord.Channel):
    """ Download messages. """
    messages = stored_messages[channel.id]  # type: deque

    # We only want to log messages when there are none
    # Any messages after this logging will be logged in the on_message event
    if messages:
        return

    # Make sure not to download messages twice by setting this handy task
    update_task.clear()

    # Download logged messages
Пример #18
0
Commands:
    pasta
"""

from random import choice
from difflib import get_close_matches

import discord
import asyncio

from pcbot import Config, Annotate
import plugins
client = plugins.client  # type: discord.Client


pastas = Config("pastas", data={})


@plugins.command(aliases="paste")
async def pasta(message: discord.Message, name: Annotate.LowerContent):
    """ Use copypastas. Don't forget to enclose the copypasta in quotes: `"pasta goes here"` for multiline
        pasta action. You also need quotes around `<name>` if it has any spaces. """
    # Display a random pasta
    assert not name == ".", choice(list(pastas.data.values()))

    # We don't use spaces in pastas at all
    parsed_name = name.replace(" ", "")

    # Pasta might not be in the set
    assert parsed_name in pastas.data, "Pasta `{}` is undefined.\nPerhaps you meant: `{}`?".format(
        name, ", ".join(get_close_matches(parsed_name, pastas.data.keys(), cutoff=0.5)))
Пример #19
0
More to come?

Commands:
    twitch
"""

import discord
import logging
from datetime import datetime

from pcbot import utils, Config
import plugins
from plugins.twitchlib import twitch
client = plugins.client  # type: discord.Client

twitch_config = Config("twitch-config", data=dict(servers={}))


@plugins.command(name="twitch")
async def twitch_group(message: discord.Message, _: utils.placeholder):
    """ Administrative commands for twitch functions. Notifies when discord says you're streaming. """
    pass


@twitch_group.command(name="channels", permissions="manage_server")
async def notify_channels(message: discord.Message,
                          *channels: discord.Channel):
    """ Specify channels to notify when a member goes live, or use no arguments to disable. """
    if message.server.id not in twitch_config.data["servers"]:
        twitch_config.data["servers"][message.server.id] = {}
Пример #20
0
""" API wrapper for twitch.tv. """

import re

import discord

from pcbot import utils, Config

twitch_config = Config("twitch-api", data=dict(ids={}, client_id=None))

# Define twitch API info
client_id = twitch_config.data["client_id"] or ""
api_url = "https://api.twitch.tv/kraken/"

url_pattern = re.compile(r"^https://www.twitch.tv/(?P<name>.+)$")


class RequestFailed(Exception):
    """ For when the api request fails. """
    pass


class UserNotResolved(Exception):
    """ For when a name isn't resolved. """
    pass


async def request(endpoint: str=None, **params):
    """ Perform a request using the twitch kraken v5 API.

    If the url key is not given, the request is sent to the root URL.
Пример #21
0
from pcbot import owner, Annotate, Config, get_command, format_exception


commands = {
    "help": "!help [command]",
    "setowner": None,
    "stop": "!stop",
    "game": "!game <name ...>",
    "do": "!do <python code ...>",
    "eval": "!eval <expression ...>",
    "plugin": "!plugin [reload | load | unload] [plugin]",
    "lambda": "!lambda [add <trigger> <python code> | [remove | enable | disable | source] <trigger>]"
}


lambdas = Config("lambdas", data={})
lambda_blacklist = []


def get_formatted_code(code):
    """ Format code from markdown format. This will filter out markdown code
    and give the executable python code, or return a string that would raise
    an error when it's executed by exec() or eval(). """
    match = re.match(r"^(?P<capt>`*)(?:[a-z]+\n)?(?P<code>.+)(?P=capt)$", code, re.DOTALL)

    if match:
        code = match.group("code")

        if not code == "`":
            return code