コード例 #1
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

コード例 #2
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

コード例 #3
0
    mute
    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()
コード例 #4
0
ファイル: twitch.py プロジェクト: Jeglet/pcbot
""" 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.
コード例 #5
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
コード例 #6
0
ファイル: builtin.py プロジェクト: EnderCapitalG/pcbot
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.server_command_prefix(message.server)

    # Display the specific command
    if command:
        if command.startswith(command_prefix):
            command = command[len(command_prefix):]
コード例 #7
0
ファイル: music.py プロジェクト: likewhoa/PCBOT
Commands:
    music
"""

from collections import namedtuple, deque
from traceback import print_exc
from typing import Dict

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(default_search="auto",
                          quiet=True,
                          nocheckcertificate=True)

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():
    discord.opus.load_opus('libopus-0.x64.dll')

Song = namedtuple("Song", "channel player requester")

コード例 #8
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
コード例 #9
0
ファイル: blacklist.py プロジェクト: stoz/PCBOT
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. 
コード例 #10
0
ファイル: alias.py プロジェクト: stoz/PCBOT
import asyncio

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 message.author.id not in aliases.data:
        aliases.data[message.author.id] = {}

    # Set options
    aliases.data[message.author.id][trigger if case_sensitive else trigger.
コード例 #11
0
ファイル: summary.py プロジェクト: NoxDraconem/pcbot
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_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", "+strict")

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()
コード例 #12
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
コード例 #13
0
try:
    from PIL import Image
except:
    resize = False
    logging.warn(
        "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]

# 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):
コード例 #14
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.**"
コード例 #15
0
ファイル: basic.py プロジェクト: stoz/PCBOT
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.display_name}** rolls `{1}`.".format(message.author, rolled))


@plugins.command()
async def avatar(message: discord.Message,
コード例 #16
0
ファイル: osu.py プロジェクト: NoxDraconem/pcbot
from plugins.twitchlib import twitch

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*
        update_interval=30,  # The sleep time in seconds between updates
        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 = osu_config.data.get("update_interval", 30)
time_elapsed = 0  # The registered time it takes to process all information between updates (changes each update)
コード例 #17
0
Commands:
    pasta
"""

from copy import copy
from difflib import get_close_matches
from random import choice

import discord
import asyncio

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

pastas = Config("pastas", data={})
pasta_cache = {}  # list of generate_pasta tuples to cache
embed_color = discord.Color.dark_grey()


async def generate_pasta(name: str):
    """ Generate a pasta embed. """
    # Return the optionally cached result
    if name in pasta_cache:
        return pasta_cache[name]

    # Choose a random pasta when the name is .
    if name == ".":
        name = choice(list(pastas.data.values()))

    # Remove spaces as pastas are space independent