def execute(self, code: str, message: Message) -> CodeOutput:
dmepath = await self.make_project(code)
proc = await create_subprocess_exec("DreamMaker", dmepath, stdout=asyncio.subprocess.PIPE) # type: asyncio.Process
await proc.wait()
data = await proc.stdout.read() # type: bytes
compile_log = data.decode("ascii") # type: str
out = CodeOutput() # type: CodeOutput
out.compile_output = compile_log
if proc.returncode:
out.state = CodeHandlerState.failed_compile
return out
else:
out.state = CodeHandlerState.compiled
proc = await create_subprocess_exec("DreamDaemon", dmepath + "b", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
await proc.wait()
data = await proc.stderr.read() + await proc.stdout.read()
log = data.decode("ascii") # type: str
out.output = log
return out
python类Message()的实例源码
def on_message(message):
"""
Event handler, fires when a message is received in the server.
:param message: discord.Message object containing the received message
"""
try:
if message.content.startswith(pm.botPreferences.commandPrefix) and client.user.id != message.author.id:
# Send the received message off to the Plugin Manager to handle the command
words = message.content.partition(' ')
await pm.handle_command(message, words[0][len(pm.botPreferences.commandPrefix):], words[1:])
elif message.server is not None:
await pm.handle_message(message)
except Exception as e:
await client.send_message(message.channel, "Error: " + str(e))
if pm.botPreferences.get_config_value("client", "debug") == "1":
traceback.print_exc()
def clear(self, ctx: commands.Context, number: int, member: discord.Member = None) -> None:
"""
Purges messages from the channel
:param ctx: The message context
:param number: The number of messages to purge
:param member: The member whose messages will be cleared
"""
if number < 1:
await command_error(ctx, "You must attempt to purge at least 1 message!")
return
def predicate(msg: discord.Message) -> bool:
return msg == ctx.message or member is None or msg.author == member
if number <= 100:
# Add 1 to limit to include command message, subtract 1 from the return to not count it.
msgs = await self.bot.purge_from(ctx.message.channel, limit=number + 1, check=predicate)
send(self.bot, '{} message{} cleared.'.format(len(msgs) - 1, "s" if len(msgs) - 1 != 1 else ""),
ctx.message.channel, True)
else:
await command_error(ctx, 'Cannot delete more than 100 messages at a time.')
def on_message(self, msg: discord.Message):
"""
When a message is recieved
:param msg: the message
"""
if msg.author == self.client.user: # Prevent a feedback loop
return
for listener in self.commands:
if listener.registry is not self: # Ensure that the listener is registered to me
continue
if listener.is_triggered_message(msg) and (listener.overrides_mute() or not self.is_muted(msg.author)):
self.logger.debug('Triggered message: {}'.format(listener))
await listener.on_message(msg)
if msg.channel.is_private and listener.is_triggered_private_message(msg):
self.logger.debug('Triggered private message: {}'.format(listener))
await listener.on_private_message(msg)
def on_private_message(self, msg: discord.Message):
categories = {}
for c in self.registry.commands:
print(c)
help = c.get_help(msg)
if help is not None:
cat = help.category
if cat not in categories:
categories[cat] = []
categories[cat].append(help)
output = '__**Available commands:**__\n\n'
for cat, helps in sorted(categories.items()):
output += '**{}**\n'.format(cat)
for h in helps:
output += '`{title}` {desc}\n'.format(title=h.title, desc=h.desc)
output += '\n'
await self.client.send_message(msg.channel, output)
def on_message(self, m: Message):
if m.channel.is_private:
logger.warn("PM's not yet implemented")
logger.info("%s in chat with %s: %s",
m.author.display_name,
str.join(", ",
(r.display_name
for r in m.channel.recipients)),
m.clean_content)
return
ss = await self.get_server_settings(m.server)
if ss.should_be_notified(m):
await config.send_notification(self, m)
logger.debug("Running %d event handlers for on_message" %
len(self.event_handlers["on_message"]))
for f in self.event_handlers['on_message']:
f(m)
def setowner(message: discord.Message):
""" Set the bot owner. Only works in private messages. """
if not message.channel.is_private:
return
assert not plugins.owner_cfg.data, "An owner is already set."
owner_code = str(random.randint(100, 999))
logging.critical("Owner code for assignment: {}".format(owner_code))
await client.say(message,
"A code has been printed in the console for you to repeat within 60 seconds.")
user_code = await client.wait_for_message(timeout=60, channel=message.channel, content=owner_code)
assert user_code, "You failed to send the desired code."
if user_code:
await client.say(message, "You have been assigned bot owner.")
plugins.owner_cfg.data = message.author.id
plugins.owner_cfg.save()
def do(message: discord.Message, python_code: Annotate.Code):
""" Execute python code. """
code_globals.update(dict(message=message, client=client,
author=message.author, server=message.server, channel=message.channel))
# Create an async function so that we can await it using the result of eval
python_code = "async def do_session():\n " + "\n ".join(line for line in python_code.split("\n"))
try:
exec(python_code, code_globals)
except SyntaxError as e:
await client.say(message, "```" + utils.format_syntax_error(e) + "```")
return
before = datetime.now()
try:
result = await eval("do_session()", code_globals)
except Exception as e:
await client.say(message, "```" + utils.format_exception(e) + "```")
else:
if result:
await send_result(message.channel, result, datetime.now() - before)
def reload(message: discord.Message, *names: str.lower):
""" Reloads all plugins or the specified plugin. """
if names:
reloaded = []
for name in names:
if not plugins.get_plugin(name):
await client.say(message, "`{}` is not a plugin.".format(name))
continue
# The plugin entered is valid so we reload it
await plugins.save_plugin(name)
await plugins.call_reload(name)
reloaded.append(name)
if reloaded:
await client.say(message, "Reloaded plugin{} `{}`.".format(
"s" if len(reloaded) > 1 else "", ", ".join(reloaded)))
else:
# Reload all plugins
await plugins.save_plugins()
for plugin_name in plugins.all_keys():
await plugins.call_reload(plugin_name)
await client.say(message, "All plugins reloaded.")
def import_(message: discord.Message, module: str, attr: str=None):
""" Import the specified module. Specifying `attr` will act like `from attr import module`.
If the given attribute starts with a colon :, the name for the module will be defined as
whatever follows the colon character. If nothing follows, the last subcommand in the module
is used.
"""
try:
name = import_module(module, attr)
except ImportError:
await client.say(message, "Unable to import `{}`.".format(module))
except KeyError:
await client.say(message, "Unable to import `{}` from `{}`.".format(attr, module))
else:
# There were no errors when importing, so we add the name to our startup imports
lambda_config.data["imports"].append((module, attr))
lambda_config.save()
await client.say(message, "Imported and setup `{}` for import.".format(name))
def bot_hub(message: discord.Message):
""" Display basic information. """
app_info = await client.application_info()
await client.say(message, "**{ver}** - **{name}** ```elm\n"
"Owner : {owner}\n"
"Up : {up} UTC\n"
"Servers : {servers}```"
"{desc}".format(
ver=config.version, name=app_info.name,
repo="https://github.com/{}".format(config.github_repo),
owner=str(app_info.owner),
up=client.time_started.strftime("%d-%m-%Y %H:%M:%S"),
servers=len(client.servers),
desc=app_info.description.replace("\\n", "\n")
))
def permission(*perms: str):
""" Decorator that runs the command only if the author has the specified permissions.
perms must be a string matching any property of discord.Permissions.
NOTE: this function is deprecated. Use the command 'permissions' attribute instead.
"""
def decorator(func):
@wraps(func)
async def wrapped(message: discord.Message, *args, **kwargs):
member_perms = message.author.permissions_in(message.channel)
if all(getattr(member_perms, perm, False) for perm in perms):
await func(message, *args, **kwargs)
return wrapped
return decorator
def role(*roles: str):
""" Decorator that runs the command only if the author has the specified Roles.
roles must be a string representing a role's name.
NOTE: this function is deprecated. Use the command 'roles' attribute instead.
"""
def decorator(func):
@wraps(func)
async def wrapped(message: discord.Message, *args, **kwargs):
member_roles = [r.name for r in message.author.roles[1:]]
if any(r in member_roles for r in roles):
await func(message, *args, **kwargs)
return wrapped
return decorator
def complete_config(message: discord.Message):
""" Return the correct config info using the given message object.
:param message: discord.Message to determine complete config data.
:return: BlacklistConfig
"""
if message.channel.id in blacklist_cache:
return blacklist_cache[message.channel.id]
# Start with global, overwrite with server, overwrite with channel
data = {}
update_data(data, "global")
update_data(data, "server", message.server.id)
update_data(data, "channel", message.channel.id)
valid_config = make_config_object(data)
# Add the found config to the channel cache, considering this will always be the channel override
blacklist_cache[message.channel.id] = valid_config
return valid_config
def delete_message(message: discord.Message, response: str, pattern: str):
""" Remove the message and send a response if there is one.
:param message: The discord message to delete.
:param response: The optional response to send, found in a BlacklistConfig.
:param pattern: The match pattern found in the deleted message, optional for the response.
"""
await client.delete_message(message)
if response:
# Parse the response message by replacing keywords
response = response \
.replace("{user}", message.author.display_name) \
.replace("{mention}", message.author.mention) \
.replace("{channel}", message.channel.mention) \
.replace("{server}", message.server.name) \
.replace("{pattern}", pattern)
await client.send_message(message.channel, response)
def when(message: discord.Message, *time, timezone: tz_arg="UTC"):
""" Convert time from specified timezone or UTC to formatted string of e.g.
`2 hours from now`. """
timezone_name = timezone
if time:
dt, timezone = await init_dt(message, " ".join(time), timezone)
if dt is None or timezone is None:
return
await client.say(message, format_when(dt, timezone_name))
else:
timezone = reverse_gmt(timezone)
dt = pendulum.now(tz=timezone)
await client.say(message, "`{} {}` is **UTC{}{}**.".format(
dt.format(dt_format), timezone_name,
"-" if dt.offset_hours < 0 else ("+" if dt.offset_hours > 0 else ""),
abs(dt.offset_hours) if dt.offset_hours else "",
))
def create(message: discord.Message, tag: tag_arg, *time, timezone: tz_arg="UTC"):
""" Create a countdown with the specified tag, using the same format as `{pre}when`. """
assert tag not in time_cfg.data["countdown"], "Countdown with tag `{}` already exists.".format(tag)
timezone_name = timezone
dt, timezone = await init_dt(message, " ".join(time), timezone)
seconds = int((dt - pendulum.now(tz=timezone)).total_seconds())
assert seconds > 0, "A countdown has to be set in the future."
cd = dict(time=dt.to_datetime_string(), tz=timezone, tz_name=timezone_name, tag=tag,
author=message.author.id, channel=message.channel.id)
time_cfg.data["countdown"][tag] = cd
time_cfg.save()
await client.say(message, "Added countdown with tag `{}`.".format(tag))
client.loop.create_task(wait_for_reminder(cd, seconds))
def resize(message: discord.Message, image_arg: image, resolution: parse_resolution, *options,
extension: str.lower=None):
""" Resize an image with the given resolution formatted as `<width>x<height>`
or `*<scale>` with an optional extension. """
if extension:
image_arg.set_extension(extension)
# Generate a new image based on the scale
if resolution[1] == 0:
w, h = image_arg.object.size
scale = resolution[0]
assert w * scale < 3000 and h * scale < 3000, "**The result image must be less than 3000 pixels in each axis.**"
resolution = (int(w * scale), int(h * scale))
# Resize and upload the image
image_arg.modify(Image.Image.resize, resolution, Image.NEAREST if "-nearest" in options else Image.ANTIALIAS)
await send_image(message, image_arg)
def gamemode(message: discord.Message, mode: api.GameMode.get_mode):
""" Sets the command executor's gamemode.
Gamemodes are: `{modes}`. """
assert message.author.id in osu_config.data["profiles"], \
"No osu! profile assigned to **{}**!".format(message.author.name)
user_id = osu_config.data["profiles"][message.author.id]
assert await has_enough_pp(u=user_id, m=mode.value, type="id"), \
"**Your pp in {} is less than the required {}pp.**".format(mode.name, minimum_pp_required)
osu_config.data["mode"][message.author.id] = mode.value
osu_config.save()
# Clear the scores when changing mode
if message.author.id in osu_tracking:
del osu_tracking[message.author.id]
await client.say(message, "Set your gamemode to **{}**.".format(mode.name))
def info(message: discord.Message, member: discord.Member=Annotate.Self):
""" Display configuration info. """
# Make sure the member is assigned
assert member.id in osu_config.data["profiles"], "No osu! profile assigned to **{}**!".format(member.name)
user_id = osu_config.data["profiles"][member.id]
mode = get_mode(member.id)
update_mode = get_update_mode(member.id)
e = discord.Embed(color=member.color)
e.set_author(name=member.display_name, icon_url=member.avatar_url, url=host + "u/" + user_id)
e.add_field(name="Game Mode", value=mode.name)
e.add_field(name="Notification Mode", value=update_mode.name)
e.add_field(name="Playing osu!", value="YES" if member.id in osu_tracking.keys() else "NO")
await client.send_message(message.channel, embed=e)
def notify(message: discord.Message, mode: UpdateModes.get_mode):
""" Sets the command executor's update notification mode. This changes
how much text is in each update, or if you want to disable them completely.
Update modes are: `{modes}`. """
assert message.author.id in osu_config.data["profiles"], \
"No osu! profile assigned to **{}**!".format(message.author.name)
osu_config.data["update_mode"][message.author.id] = mode.name
osu_config.save()
# Clear the scores when disabling mode
if message.author.id in osu_tracking and mode == UpdateModes.Disabled:
del osu_tracking[message.author.id]
await client.say(message, "Set your update notification mode to **{}**.".format(mode.name.lower()))
def mark(message: discord.Message, plugin: plugin_in_req, req_id: get_req_id):
""" Toggles marking a feature request as complete.
See `{pre}plugin` for a list of plugins. """
# Test and reply if feature by requested id doesn't exist
assert feature_exists(plugin, req_id), "There is no such feature."
req = feature_reqs.data[plugin][req_id]
# Mark or unmark the feature request by adding or removing +++ from the end (slightly hacked)
if not req.endswith("+++"):
feature_reqs.data[plugin][req_id] += "+++"
feature_reqs.save()
await client.say(message, "Marked feature with `{}` id **#{}**.".format(plugin, req_id + 1))
else:
feature_reqs.data[plugin][req_id] = req[:-3]
feature_reqs.save()
await client.say(message, "Unmarked feature with `{}` id **#{}**.".format(plugin, req_id + 1))
def on_message_delete(message: discord.Message):
""" Update the changelog with deleted messages. """
changelog_channel = get_changelog_channel(message.server)
# Don't log any message the bot deleted
for m in client.last_deleted_messages:
if m.id == message.id:
return
if not changelog_channel:
return
if message.channel == changelog_channel:
return
if message.author == client.user:
return
await client.send_message(
changelog_channel,
"{0.author.mention}'s message was deleted in {0.channel.mention}:\n{0.clean_content}".format(message)
)
def filter_type(message: discord.Message, slot_1: str.lower, slot_2: str.lower=None):
matched_pokemon = []
assert_type(slot_1, message.server)
# Find all pokemon with the matched criteria
if slot_2:
assert_type(slot_2, message.server)
# If two slots are provided, search for pokemon with both types matching
for pokemon in pokedex.values():
if pokemon["types"] == [slot_1, slot_2]:
matched_pokemon.append(pokemon["locale_name"])
else:
# All pokemon have a type in their first slot, so check if these are equal
for pokemon in pokedex.values():
if pokemon["types"][0] == slot_1:
matched_pokemon.append(pokemon["locale_name"])
# There might not be any pokemon with the specified types
assert matched_pokemon, "Looks like there are no pokemon of type **{}**!".format(format_type(slot_1, slot_2))
await client.say(message, "**Pokemon with type {}**: ```\n{}```".format(
format_type(slot_1, slot_2), ", ".join(sorted(matched_pokemon))))
def scalefactor(message: discord.Message, factor: float=default_scale_factor):
""" Set the image scaling factor for your server. If no factor is given, the default is set. /
**This command requires the `Manage Server` permission.**"""
assert not factor == 0, "If you wish to disable images, remove the `Attach Files` permission from this bot."
assert factor <= max_scale_factor, "The factor **{}** is too high **(max={})**.".format(factor, max_scale_factor)
assert min_scale_factor <= factor, "The factor **{}** is too low **(min={})**.".format(factor, min_scale_factor)
if message.server.id not in pokedex_config.data:
pokedex_config.data[message.server.id] = {}
# Handle specific scenarios
if factor == default_scale_factor:
if "scale-factor" in pokedex_config.data[message.server.id]:
del pokedex_config.data[message.server.id]["scale-factor"]
reply = "Pokédex image scale factor reset to default: **{factor}**."
else:
reply = "Pokédex image scale factor is **{factor}** (default)."
else:
pokedex_config.data[message.server.id]["scale-factor"] = factor
reply = "Pokédex image scale factor set to **{factor}**."
pokedex_config.save()
await client.say(message, reply.format(factor=factor))
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.lower()] = dict(
text=text,
anywhere=anywhere,
case_sensitive=case_sensitive,
delete_message=delete_message
)
aliases.save()
m = "**Alias assigned.** Type `{}`{} to trigger the alias."
await client.say(message, m.format(trigger, " anywhere in a message" if anywhere else ""))
def execute(cmd, message: discord.Message, *args, **kwargs):
""" Execute a command specified by name, alias or command object.
This is really only useful as a shortcut for other commands.
:param cmd: either plugins.Command or str
:param message: required message object in order to execute a command
:param args, kwargs: any arguments passed into the command.
:raises: NameError when command does not exist.
"""
# Get the command object if the given command represents a name
if type(cmd) is not Command:
cmd = get_command(cmd, config.server_case_sensitive_commands(message.server))
try:
await cmd.function(message, *args, **kwargs)
except AttributeError:
raise NameError("{} is not a command".format(cmd))
def define(message: discord.Message, term: Annotate.LowerCleanContent):
""" Defines a term using Urban Dictionary. """
json = await utils.download_json("http://api.urbandictionary.com/v0/define", term=term)
assert json["list"], "Could not define `{}`.".format(term)
definitions = json["list"]
msg = ""
# Send any valid definition (length of message < 2000 characters)
for definition in definitions:
# Format example in code if there is one
if definition.get("example"):
definition["example"] = "```{}```".format(definition["example"])
# Format definition
msg = "**{word}**:\n{definition}{example}".format(**definition)
# If this definition fits in a message, break the loop so that we can send it
if len(msg) <= 2000:
break
# Cancel if the message is too long
assert len(msg) <= 2000, "Defining this word would be a bad idea."
await client.say(message, msg)
def run_coro(self, event):
channel = self.bot.get_channel(event.channel)
try:
server = channel.server
prefix = self.bot.settings.get_prefixes(server)[0]
except AttributeError:
log.debug("Channel no longer found, not running scheduled event.")
return
data = {}
data['timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%S%z", time.gmtime())
data['id'] = randint(10**(17), (10**18) - 1)
data['content'] = prefix + event.command
data['channel'] = self.bot.get_channel(event.channel)
data['author'] = {'id': event.author}
data['nonce'] = randint(-2**32, (2**32) - 1)
data['channel_id'] = event.channel
data['reactions'] = []
fake_message = discord.Message(**data)
# coro = self.bot.process_commands(fake_message)
log.info("Running '{}' in {}".format(event.name, event.server))
# self.bot.loop.create_task(coro)
self.bot.dispatch('message', fake_message)
def on_message_edit(self, before: discord.Message, after: discord.Message):
# if message author was a bot, or the embeds were added by discord, bail
if before.author.bot or before.content == after.content:
return
# if this channel isn't publicly visible or we aren't tracking edits, bail
if (not await is_publicly_visible(self.bot, before.channel) or
await self.bot.config_is_set(before.guild, 'modlog_notrack_edits')):
return
# truncate :blobsweats:
m_before = utils.prevent_codeblock_breakout(utils.truncate(before.content, 900))
m_after = utils.prevent_codeblock_breakout(utils.truncate(after.content, 900))
# format
fmt = (f'\N{MEMO} Message by {describe(before.author)} in {describe(before.channel, mention=True)} edited: '
f'```\n{m_before}\n``` to ```\n{m_after}\n```')
await self.log(before.guild, fmt)