mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Added new docs for Channels and Msg
This commit is contained in:
parent
43651ac867
commit
7e2a446bda
9 changed files with 474 additions and 193 deletions
|
|
@ -1,3 +1,308 @@
|
|||
# Channels
|
||||
|
||||
TODO: Channels are covered in [Communications](./Communications) right now.
|
||||
In a multiplayer game, players often need other means of in-game communication
|
||||
than just walking from room to room using `say` or `emote`.
|
||||
|
||||
_Channels_ are Evennia's system for letting the server act as a fancy chat
|
||||
program. When a player is connected to a channel, sending a message to it will
|
||||
automatically distribute it to every other subscriber.
|
||||
|
||||
Channels can be used both for chats between [Accounts](./Accounts) and between
|
||||
[Objects](./Objects) (usually Characters) and (in principle) a mix of the two.
|
||||
Chats could be both OOC (out-of-character) or IC (in-charcter) in nature. Some
|
||||
examples:
|
||||
|
||||
- A support channel for contacting staff (OOC)
|
||||
- A general chat for discussing anything and foster community (OOC)
|
||||
- Admin channel for private staff discussions (OOC)
|
||||
- Private guild channels for planning and organization (IC/OOC depending on game)
|
||||
- Cyberpunk-style retro chat rooms (IC)
|
||||
- In-game radio channels (IC)
|
||||
- Group telephathy (IC)
|
||||
- Walkie talkies (IC)
|
||||
|
||||
```versionchanged:: 1.0
|
||||
|
||||
Channel system changed to use a central 'channel' command and nicks instead of
|
||||
auto-generated channel-commands and -cmdset. ChannelHandler was removed.
|
||||
|
||||
```
|
||||
|
||||
## Using channels in-game
|
||||
|
||||
In the default command set, channels are all handled via the mighty
|
||||
[channel command](api:evennia.commands.default.comms.CmdChannel), `channel` (or
|
||||
`chan`). By default, this command will assume all entities dealing with
|
||||
channels are `Accounts`.
|
||||
|
||||
### Viewing, joining and creating channels
|
||||
|
||||
channel - shows your subscriptions
|
||||
channel/all - shows all subs available to you
|
||||
channel/who - shows who subscribes to this channel
|
||||
|
||||
To join/unsub a channel do
|
||||
|
||||
channel/sub channelname
|
||||
channel/unsub channelname
|
||||
|
||||
If you temporarily don't want to hear the channel for a while (without actually
|
||||
unsubscribing), you can mute it:
|
||||
|
||||
channel/mute channelname
|
||||
channel/unmute channelname
|
||||
|
||||
To create/destroy a new channel you can do
|
||||
|
||||
channel/create channelname;alias;alias = description
|
||||
channel/destroy channelname
|
||||
|
||||
Aliases are optional but can be good for obvious shortcuts everyone may want to
|
||||
use. The description is used in channel-listings. You will automatically join a
|
||||
channel you created and will be controlling it.
|
||||
|
||||
### Chat on channels
|
||||
|
||||
To speak on a channel, do
|
||||
|
||||
channel public Hello world!
|
||||
|
||||
If the channel-name has spaces in it, you need to use a '`=`':
|
||||
|
||||
channel rest room = Hello world!
|
||||
|
||||
Now, this is more to type than we'd like, so when you join a channel, the
|
||||
system automatically sets up an personal alias so you can do this instead:
|
||||
|
||||
public Hello world
|
||||
|
||||
```warning::
|
||||
|
||||
This shortcut will not work if the channel-name has spaces in it.
|
||||
So channels with long names should make sure to provide a one-word alias as
|
||||
well.
|
||||
```
|
||||
|
||||
Any user can make up their own channel aliases:
|
||||
|
||||
channel/alias public = foo;bar
|
||||
|
||||
You can now just do
|
||||
|
||||
foo Hello world!
|
||||
bar Hello again!
|
||||
|
||||
But you can also use your alias with the `channel` command:
|
||||
|
||||
channel foo Hello world!
|
||||
|
||||
> What happens when aliasing is that a [nick](./Nicks) is created that maps your
|
||||
> alias + argument onto calling the `channel` command. So when you enter `foo hello`,
|
||||
> what the server sees is actually `channel foo = hello`. The system is also
|
||||
> clever enough to know that whenever you search for channels, your channel-nicks
|
||||
> should first be considered.
|
||||
|
||||
You can check if you missed something by viewing the channel's scrollback with
|
||||
|
||||
channel/history public
|
||||
|
||||
This retrieves the last 20 lines of text (also from a time when you were
|
||||
offline). You can step further back by specifying how many lines back to start:
|
||||
|
||||
channel/history public = 30
|
||||
|
||||
This again retrieve 20 lines, but starting 30 lines back (so you'll get lines
|
||||
30-50 counting backwards).
|
||||
|
||||
|
||||
### Channel administration
|
||||
|
||||
If you control the channel (because you are an admin or created it) you have the
|
||||
ability to control who can access it by use of [locks](./Locks):
|
||||
|
||||
channel/lock buildchannel = listen:all();send:perm(Builders)
|
||||
|
||||
Channels use three lock-types by default:
|
||||
|
||||
- `listen` - who may listen to the channel. Users without this access will not
|
||||
even be able to join the channel and it will not appear in listings for them.
|
||||
- `send` - who may send to the channel.
|
||||
- `control` - this is assigned to you automatically when you create the channel. With
|
||||
control over the channel you can edit it, boot users and do other management tasks.
|
||||
|
||||
If you control a channel you can also kick people off it:
|
||||
|
||||
channel/boot mychannel = annoyinguser123 : stop spamming!
|
||||
|
||||
The last part is an optional reason to send to the user before they are booted.
|
||||
You can give a comma-separated list of channels to kick the same user from all
|
||||
those channels at once. The user will be unsubbed from the channel and all
|
||||
their aliases will be wiped. But they can still rejoin if they like.
|
||||
|
||||
channel/ban mychannel = annoyinguser123 : spammed too much
|
||||
channel/ban - view bans
|
||||
channel/unban mychannel = annoyinguser123
|
||||
|
||||
The optional reason at the end shows in the banlist
|
||||
Banning adds the user to the channels blacklist. This means they will not be
|
||||
able to rejoin if you boot them. You will need to run `channel/boot` to
|
||||
actually kick them.
|
||||
|
||||
See the [Channel command](api:evennia.commands.default.comms.CmdChannel) api
|
||||
docs (and in-game help) for more details.
|
||||
|
||||
|
||||
## Allowing Characters to use Channels
|
||||
|
||||
The default `channel` command ([evennia.commands.default.comms.CmdChannel](api:evennia.commands.default.comms.CmdChannel))
|
||||
sits in the `Account` [command set](./Command-Sets). It is set up such that it will
|
||||
always operate on `Accounts`, even if you were to add it to the
|
||||
`CharacterCmdSet`.
|
||||
|
||||
It's a one-line change to make this command accept non-account callers. But for
|
||||
convenience we provide a version for Characters/Objects. Just import
|
||||
[evennia.commands.default.comms.CmdObjectChannel](api:evennia.commands.default.comms.CmdObjectChannel)
|
||||
and inherit from that instead.
|
||||
|
||||
## Customizing channel output and behavior
|
||||
|
||||
When distributing a message, the channel will call a series of hooks on itself
|
||||
and (more importantly) on each recipient. So you can customize things a lot by
|
||||
just modifying hooks on your normal Object/Account typeclasses.
|
||||
|
||||
Internally, the message is sent with
|
||||
`channel.msg(message, senders=sender, bypass_mute=False, **kwargs)`, where
|
||||
`bypass_mute=True` means the message ignores muting (good for alerts or if you
|
||||
delete the channel etc) and `**kwargs` are any extra info you may want to pass
|
||||
to the hooks. The `senders` (it's always only one in the default implementation
|
||||
but could in principle be multiple) and `bypass_mute` are part of the `kwargs`
|
||||
below:
|
||||
|
||||
1. `channel.at_pre_msg(message, **kwargs)`
|
||||
2. For each recipient:
|
||||
- `message = recipient.at_pre_channel_msg(message, channel, **kwargs)` -
|
||||
allows for the message to be tweaked per-receiver (for example coloring it depending
|
||||
on the users' preferences). If this method returns `False/None`, that
|
||||
recipient is skipped.
|
||||
- `recipient.channel_msg(message, channel, **kwargs)` - actually sends to recipient.
|
||||
- `recipient.at_post_channel_msg(message, channel, **kwargs)` - any post-receive effects.
|
||||
3. `channel.at_post_channel_msg(message, **kwargs)`
|
||||
|
||||
Note that `Accounts` and `Objects` both have their have separate sets of hooks.
|
||||
So make sure you modify the set actually used by your subcribers (or both).
|
||||
Default channels all use `Account` subscribers.
|
||||
|
||||
## Channels in code
|
||||
|
||||
For most common changes, the default channel, the recipient hooks and possibly
|
||||
overriding the `channel` command will get you very far. But you can also tweak
|
||||
channels themselves.
|
||||
|
||||
Channels are [Typeclassed](./Typeclasses) entities. This means they are
|
||||
persistent in the database, can have [attributes](./Attributes) and [Tags](./Tags)
|
||||
and can be easily extended.
|
||||
|
||||
To change which channel typeclass Evennia uses for default commands, change
|
||||
`settings.BASE_CHANNEL_TYPECLASS`. The base command class is
|
||||
[`evennia.comms.comms.DefaultChannel`](api:evennia.comms.comms.DefaultChannel).
|
||||
There is an empty child class in `mygame/typeclasses/channels.py`, same
|
||||
as for other typelass-bases.
|
||||
|
||||
In code you create a new channel with `evennia.create_channel` or
|
||||
`Channel.create`:
|
||||
|
||||
```python
|
||||
from evennia import create_channel, search_object
|
||||
from typeclasses.channels import Channel
|
||||
|
||||
channel = create_channel("my channel", aliases=["mychan"], locks=..., typeclass=...)
|
||||
# alternative
|
||||
channel = Channel.create("my channel", aliases=["mychan"], locks=...)
|
||||
|
||||
# connect to it
|
||||
me = search_object(key="Foo")[0]
|
||||
channel.connect(me)
|
||||
|
||||
# send to it (this will trigger the channel_msg hooks described earlier)
|
||||
channel.msg("Hello world!", senders=me)
|
||||
|
||||
# view subscriptions (the SubscriptionHandler handles all subs under the hood)
|
||||
channel.subscriptions.has(me) # check we subbed
|
||||
channel.subscriptions.all() # get all subs
|
||||
channel.subscriptions.online() # get only subs currently online
|
||||
channel.subscriptions.clear() # unsub all
|
||||
|
||||
# leave channel
|
||||
channel.disconnect(me)
|
||||
|
||||
# permanently delete channel (will unsub everyone)
|
||||
channel.delete()
|
||||
|
||||
```
|
||||
|
||||
The Channel's `.connect` method will accept both `Account` and `Object` subscribers
|
||||
and will handle them transparently.
|
||||
|
||||
The channel has many more hooks, both hooks shared with all typeclasses as well
|
||||
as special ones related to muting/banning etc. See the channel class for
|
||||
details.
|
||||
|
||||
## Channel logging
|
||||
|
||||
```versionchanged:: 0.7
|
||||
|
||||
Channels changed from using Msg to TmpMsg and optional log files.
|
||||
```
|
||||
```versionchanged:: 1.0
|
||||
|
||||
Channels stopped supporting Msg and TmpMsg, using only log files.
|
||||
```
|
||||
|
||||
The channel messages are not stored in the database. A channel is instead
|
||||
always logged to a regular text log-file
|
||||
`mygame/server/logs/channel_<channelname>.log`. This is where `channels/history channelname`
|
||||
gets its data from. A channel's log will rotate when it grows too big, which
|
||||
thus also automatically limits the max amount of history a user can view with
|
||||
`/history`.
|
||||
|
||||
### Properties on Channels
|
||||
|
||||
Channels have all the standard properties of a Typeclassed entity (`key`,
|
||||
`aliases`, `attributes`, `tags`, `locks` etc). This is not an exhaustive list;
|
||||
see the [Channel api docs](api:evennia.comms.comms.DefaultChannel) for details.
|
||||
|
||||
- `send_to_online_only` - this class boolean defaults to `True` and is a
|
||||
sensible optimization since people offline people will not see the message anyway.
|
||||
- `log_to_file` - this is a string that determines the name of the channel log file. Default
|
||||
is `"channel_{channel_key}.log"`. You should usually not change this.
|
||||
- `channel_prefix_string` - this property is a string to easily change how
|
||||
the channel is prefixed. It takes the `channel_key` format key. Default is `"[{channel_key}] "`
|
||||
and produces output like `[public] ...``.
|
||||
- `subscriptions` - this is the [SubscriptionHandler](`api:evennia.comms.comms.SubscriptionHandler`), which
|
||||
has methods `has`, `add`, `remove`, `all`, `clear` and also `online` (to get
|
||||
only actually online channel-members).
|
||||
- `wholist`, `mutelist`, `banlist` are properties that return a list of subscribers,
|
||||
as well as who are currently muted or banned.
|
||||
|
||||
Notable `Channel` hooks:
|
||||
|
||||
- `at_pre_channel_msg(message, **kwargs)` - called before sending a message, to
|
||||
modify it. Not used by default.
|
||||
- `msg(message, senders=..., bypass_mute=False, **kwargs)` - send the message onto
|
||||
the channel. The `**kwargs` are passed on into the other call hooks (also on the recipient).
|
||||
- `at_post_channel_msg(message, **kwargs)` - by default this is used to store the message
|
||||
to the log file.
|
||||
- `channel_prefix(message)` - this is called to allow the channel to prefix. This is called
|
||||
by the object/account when they build the message, so if wanting something else one can
|
||||
also just remove that call.
|
||||
- every channel message. By default it just returns `channel_prefix_string`.
|
||||
- `has_connection(subscriber)` - shortcut to check if an entity subscribes to
|
||||
this channel
|
||||
- `mute/unmute(subscriber)` - this mutes the channel for this user.
|
||||
- `ban/unban(subscriber)` - adds/remove user from banlist.
|
||||
- `connect/disconnect(subscriber)` - adds/removes a subscriber.
|
||||
- `pre_join_channel(subscriber)` - if this returns `False`, connection will be refused.
|
||||
- `post_join_channel(subscriber)` - unused by default.
|
||||
- `pre_leave_channel(subscriber)` - if this returns `False`, the user is not allowed to leave.
|
||||
- `post_leave_channel(subscriber)` - unused by default.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,113 +1,8 @@
|
|||
# Communications
|
||||
|
||||
TODO: Remove this page?
|
||||
|
||||
Apart from moving around in the game world and talking, players might need other forms of
|
||||
communication. This is offered by Evennia's `Comm` system. Stock evennia implements a 'MUX-like'
|
||||
system of channels, but there is nothing stopping you from changing things to better suit your
|
||||
taste.
|
||||
|
||||
Comms rely on two main database objects - `Msg` and `Channel`. There is also the `TempMsg` which
|
||||
mimics the API of a `Msg` but has no connection to the database.
|
||||
|
||||
## Msg
|
||||
|
||||
The `Msg` object is the basic unit of communication in Evennia. A message works a little like an
|
||||
e-mail; it always has a sender (a [Account](./Accounts)) and one or more recipients. The recipients
|
||||
may be either other Accounts, or a *Channel* (see below). You can mix recipients to send the message
|
||||
to both Channels and Accounts if you like.
|
||||
|
||||
Once created, a `Msg` is normally not changed. It is peristently saved in the database. This allows
|
||||
for comprehensive logging of communications. This could be useful for allowing senders/receivers to
|
||||
have 'mailboxes' with the messages they want to keep.
|
||||
|
||||
### Properties defined on `Msg`
|
||||
|
||||
- `senders` - this is a reference to one or many [Account](./Accounts) or [Objects](./Objects) (normally
|
||||
*Characters*) sending the message. This could also be an *External Connection* such as a message
|
||||
coming in over IRC/IMC2 (see below). There is usually only one sender, but the types can also be
|
||||
mixed in any combination.
|
||||
- `receivers` - a list of target [Accounts](./Accounts), [Objects](./Objects) (usually *Characters*) or
|
||||
*Channels* to send the message to. The types of receivers can be mixed in any combination.
|
||||
- `header` - this is a text field for storing a title or header for the message.
|
||||
- `message` - the actual text being sent.
|
||||
- `date_sent` - when message was sent (auto-created).
|
||||
- `locks` - a [lock definition](./Locks).
|
||||
- `hide_from` - this can optionally hold a list of objects, accounts or channels to hide this `Msg`
|
||||
from. This relationship is stored in the database primarily for optimization reasons, allowing for
|
||||
quickly post-filter out messages not intended for a given target. There is no in-game methods for
|
||||
setting this, it's intended to be done in code.
|
||||
|
||||
You create new messages in code using `evennia.create_message` (or
|
||||
`evennia.utils.create.create_message.`)
|
||||
|
||||
## TempMsg
|
||||
|
||||
`evennia.comms.models` also has `TempMsg` which mimics the API of `Msg` but is not connected to the
|
||||
database. TempMsgs are used by Evennia for channel messages by default. They can be used for any
|
||||
system expecting a `Msg` but when you don't actually want to save anything.
|
||||
|
||||
## Channels
|
||||
|
||||
Channels are [Typeclassed](./Typeclasses) entities, which mean they can be easily extended and their
|
||||
functionality modified. To change which channel typeclass Evennia uses, change
|
||||
settings.BASE_CHANNEL_TYPECLASS.
|
||||
|
||||
Channels act as generic distributors of messages. Think of them as "switch boards" redistributing
|
||||
`Msg` or `TempMsg` objects. Internally they hold a list of "listening" objects and any `Msg` (or
|
||||
`TempMsg`) sent to the channel will be distributed out to all channel listeners. Channels have
|
||||
[Locks](./Locks) to limit who may listen and/or send messages through them.
|
||||
|
||||
The *sending* of text to a channel is handled by a dynamically created [Command](./Commands) that
|
||||
always have the same name as the channel. This is created for each channel by the global
|
||||
`ChannelHandler`. The Channel command is added to the Account's cmdset and normal command locks are
|
||||
used to determine which channels are possible to write to. When subscribing to a channel, you can
|
||||
then just write the channel name and the text to send.
|
||||
|
||||
The default ChannelCommand (which can be customized by pointing `settings.CHANNEL_COMMAND_CLASS` to
|
||||
your own command), implements a few convenient features:
|
||||
|
||||
- It only sends `TempMsg` objects. Instead of storing individual entries in the database it instead
|
||||
dumps channel output a file log in `server/logs/channel_<channelname>.log`. This is mainly for
|
||||
practical reasons - we find one rarely need to query individual Msg objects at a later date. Just
|
||||
stupidly dumping the log to a file also means a lot less database overhead.
|
||||
- It adds a `/history` switch to view the 20 last messages in the channel. These are read from the
|
||||
end of the log file. One can also supply a line number to start further back in the file (but always
|
||||
20 entries at a time). It's used like this:
|
||||
|
||||
> public/history
|
||||
> public/history 35
|
||||
|
||||
|
||||
There are two default channels created in stock Evennia - `MudInfo` and `Public`. `MudInfo`
|
||||
receives server-related messages meant for Admins whereas `Public` is open to everyone to chat on
|
||||
(all new accounts are automatically joined to it when logging in, it is useful for asking
|
||||
questions). The default channels are defined by the `DEFAULT_CHANNELS` list (see
|
||||
`evennia/settings_default.py` for more details).
|
||||
|
||||
You create new channels with `evennia.create_channel` (or `evennia.utils.create.create_channel`).
|
||||
|
||||
In code, messages are sent to a channel using the `msg` or `tempmsg` methods of channels:
|
||||
|
||||
channel.msg(msgobj, header=None, senders=None, persistent=True)
|
||||
|
||||
The argument `msgobj` can be either a string, a previously constructed `Msg` or a `TempMsg` - in the
|
||||
latter cases all the following keywords are ignored since the message objects already contains all
|
||||
this information. If `msgobj` is a string, the other keywords are used for creating a new `Msg` or
|
||||
`TempMsg` on the fly, depending on if `persistent` is set or not. By default, a `TempMsg` is emitted
|
||||
for channel communication (since the default ChannelCommand instead logs to a file).
|
||||
|
||||
```python
|
||||
# assume we have a 'sender' object and a channel named 'mychan'
|
||||
|
||||
# manually sending a message to a channel
|
||||
mychan.msg("Hello!", senders=[sender])
|
||||
```
|
||||
|
||||
### Properties defined on `Channel`
|
||||
|
||||
- `key` - main name for channel
|
||||
- `aliases` - alternative native names for channels
|
||||
- `desc` - optional description of channel (seen in listings)
|
||||
- `keep_log` (bool) - if the channel should store messages (default)
|
||||
- `locks` - A [lock definition](./Locks). Channels normally use the access_types `send, control` and
|
||||
`listen`.
|
||||
- [Channels](./Channels) - are used for implementing in-game chat rooms.
|
||||
- [Msg](./Msg)-objects are used for storing messages in the database (email-like)
|
||||
and is a building block for implementing other game systems. It's used by the
|
||||
`page` command by default.
|
||||
|
|
|
|||
91
docs/source/Components/Msg.md
Normal file
91
docs/source/Components/Msg.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# Msg
|
||||
|
||||
The [Msg](api:evennia.comms.models.Msg) object represents a database-saved
|
||||
piece of communication. Think of it as a discrete piece of email - it contains
|
||||
a message, some metadata and will always have a sender and one or more
|
||||
recipients.
|
||||
|
||||
Once created, a Msg is normally not changed. It is persitently saved in the
|
||||
database. This allows for comprehensive logging of communications. Here are some
|
||||
good uses for `Msg` objects:
|
||||
|
||||
- page/tells (the `page` command is how Evennia uses them out of the box)
|
||||
- messages in a bulletin board
|
||||
- game-wide email stored in 'mailboxes'.
|
||||
|
||||
|
||||
```important::
|
||||
|
||||
A `Msg` does not have any in-game representation. So if you want to use them
|
||||
to represent in-game mail/letters, the physical letters would never be
|
||||
visible in a room (possible to steal, spy on etc) unless you make your
|
||||
spy-system access the Msgs directly (or go to the trouble of spawning an
|
||||
actual in-game letter-object based on the Msg)
|
||||
|
||||
|
||||
```
|
||||
|
||||
```versionchanged:: 1.0
|
||||
Channels dropped Msg-support. Now only used in `page` command by default.
|
||||
```
|
||||
|
||||
## Msg in code
|
||||
|
||||
The Msg is intended to be used exclusively in code, to build other game systems. It is _not_
|
||||
a [Typeclassed](./Typeclasses) entity, which means it cannot (easily) be overridden. It
|
||||
doesn't support Attributes (but it _does_ support [Tags](./Tags)). It tries to be lean
|
||||
and small since a new one is created for every message.
|
||||
|
||||
You create a new message with `evennia.create_message`:
|
||||
|
||||
```python
|
||||
from evennia import create_message
|
||||
message = create_message(senders, message, receivers,
|
||||
locks=..., tags=..., header=...)
|
||||
```
|
||||
|
||||
You can search for `Msg` objects in various ways:
|
||||
|
||||
|
||||
```python
|
||||
from evennia import search_message, Msg
|
||||
|
||||
# args are optional. Only a single sender/receiver should be passed
|
||||
messages = search_message(sender=..., receiver=..., freetext=..., dbref=...)
|
||||
|
||||
# get all messages for a given sender/receiver
|
||||
messages = Msg.objects.get_msg_by_sender(sender)
|
||||
messages = Msg.objects.get_msg_by_receiver(recipient)
|
||||
|
||||
```
|
||||
|
||||
### Properties on Msg
|
||||
|
||||
- `senders` - there must always be at least one sender. This is one of [Account](./Accounts), [Object](./Objects), [Script](./Scripts)
|
||||
or _external_ - which is a string uniquely identifying the sender. The latter can be used by
|
||||
a sender-system that doesn't fit into Evennia's normal typeclass-system.
|
||||
While most systems expect a single sender, it's possible to have any number of them.
|
||||
- `receivers` - these are the ones to see the Msg. These are again one of
|
||||
[Account](./Accounts), [Object](./Objects) or [Script](./Scripts). It's in principle possible to have
|
||||
zero receivers but most usages of Msg expects one or more.
|
||||
- `header` - this is an optional text field that can contain meta-information about the message. For
|
||||
an email-like system it would be the subject line. This can be independently searched, making
|
||||
this a powerful place for quickly finding messages.
|
||||
- `message` - the actual text being sent.
|
||||
- `date_sent` - this is auto-set to the time the Msg was created (and thus presumably sent).
|
||||
- `locks` - the Evennia [lock handler](./Locks). Use with `locks.add()` etc and check locks with `msg.access()`
|
||||
like for all other lockable entities. This can be used to limit access to the contents
|
||||
of the Msg. The default lock-type to check is `'read'`.
|
||||
- `hide_from` - this is an optional list of [Accounts](./Accounts) or [Objects](./Objects) that
|
||||
will not see this Msg. This relationship is available mainly for optimization
|
||||
reasons since it allows quick filtering of messages not intended for a given
|
||||
target.
|
||||
|
||||
|
||||
## TempMsg
|
||||
|
||||
[evennia.comms.models.TempMsg](api:evennia.comms.models.TempMsg) is an object
|
||||
that implements the same API as the regular `Msg`, but which has no database
|
||||
component (and thus cannot be searched). It's meant to plugged into systems
|
||||
expecting a `Msg` but where you just want to process the message without saving
|
||||
it.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
# Toc
|
||||
- [API root](api/evennia-api.rst)
|
||||
|
||||
- [Coding/Coding Introduction](Coding/Coding-Introduction)
|
||||
- [Coding/Coding Overview](Coding/Coding-Overview)
|
||||
- [Coding/Continuous Integration](Coding/Continuous-Integration)
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
- [Components/Inputfuncs](Components/Inputfuncs)
|
||||
- [Components/Locks](Components/Locks)
|
||||
- [Components/MonitorHandler](Components/MonitorHandler)
|
||||
- [Components/Msg](Components/Msg)
|
||||
- [Components/Nicks](Components/Nicks)
|
||||
- [Components/Objects](Components/Objects)
|
||||
- [Components/Outputfuncs](Components/Outputfuncs)
|
||||
|
|
|
|||
|
|
@ -152,8 +152,8 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
def msg_channel(self, channel, message, **kwargs):
|
||||
"""
|
||||
Send a message to a given channel. At this point
|
||||
any permissions should already be done.
|
||||
Send a message to a given channel. This will check the 'send'
|
||||
permission on the channel.
|
||||
|
||||
Args:
|
||||
channel (Channel): The channel to send to.
|
||||
|
|
@ -162,6 +162,10 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
|||
all channel messaging hooks for custom overriding.
|
||||
|
||||
"""
|
||||
if not channel.access(self.caller, "send"):
|
||||
caller.msg(f"You are not allowed to send messages to channel {channel}")
|
||||
return
|
||||
|
||||
channel.msg(message, senders=self.caller, **kwargs)
|
||||
|
||||
def get_channel_history(self, channel, start_index=0):
|
||||
|
|
@ -587,7 +591,7 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
|||
name = subscriber.get_display_name(caller)
|
||||
conditions = ("muted" if subscriber in mute_list else "",
|
||||
"offline" if subscriber not in online_list else "")
|
||||
conditions = (cond for cond in conditions if cond)
|
||||
conditions = [cond for cond in conditions if cond]
|
||||
cond_text = "(" + ", ".join(conditions) + ")" if conditions else ""
|
||||
who_list.append(f"{name}{cond_text}")
|
||||
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
def create(cls, key, account=None, *args, **kwargs):
|
||||
def create(cls, key, creator=None, *args, **kwargs):
|
||||
"""
|
||||
Creates a basic Channel with default parameters, unless otherwise
|
||||
specified or extended.
|
||||
|
|
@ -323,7 +323,8 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase):
|
|||
|
||||
Args:
|
||||
key (str): This must be unique.
|
||||
account (Account): Account to attribute this object to.
|
||||
creator (Account or Object): Entity to associate with this channel
|
||||
(used for tracking)
|
||||
|
||||
Keyword Args:
|
||||
aliases (list of str): List of alternative (likely shorter) keynames.
|
||||
|
|
@ -351,8 +352,8 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase):
|
|||
# Record creator id and creation IP
|
||||
if ip:
|
||||
obj.db.creator_ip = ip
|
||||
if account:
|
||||
obj.db.creator_id = account.id
|
||||
if creator:
|
||||
obj.db.creator_id = creator.id
|
||||
|
||||
except Exception as exc:
|
||||
errors.append("An error occurred while creating this '%s' object." % key)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ _GA = object.__getattribute__
|
|||
_AccountDB = None
|
||||
_ObjectDB = None
|
||||
_ChannelDB = None
|
||||
_ScriptDB = None
|
||||
_SESSIONS = None
|
||||
|
||||
# error class
|
||||
|
|
@ -54,6 +55,8 @@ def identify_object(inp):
|
|||
return inp, "object"
|
||||
elif clsname == "ChannelDB":
|
||||
return inp, "channel"
|
||||
elif clsname == "ScriptDB":
|
||||
return inp, "script"
|
||||
if isinstance(inp, str):
|
||||
return inp, "string"
|
||||
elif dbref(inp):
|
||||
|
|
@ -103,6 +106,14 @@ def to_object(inp, objtype="account"):
|
|||
return _ChannelDB.objects.get(id=obj)
|
||||
logger.log_err("%s %s %s %s %s" % (objtype, inp, obj, typ, type(inp)))
|
||||
raise CommError()
|
||||
elif objtype == "script":
|
||||
if typ == "string":
|
||||
return _ScriptDB.objects.get(db_key__iexact=obj)
|
||||
if typ == "dbref":
|
||||
return _ScriptDB.objects.get(id=obj)
|
||||
logger.log_err("%s %s %s %s %s" % (objtype, inp, obj, typ, type(inp)))
|
||||
raise CommError()
|
||||
|
||||
# an unknown
|
||||
return None
|
||||
|
||||
|
|
@ -158,48 +169,30 @@ class MsgManager(TypedObjectManager):
|
|||
except Exception:
|
||||
return None
|
||||
|
||||
def get_messages_by_sender(self, sender, exclude_channel_messages=False):
|
||||
def get_messages_by_sender(self, sender):
|
||||
"""
|
||||
Get all messages sent by one entity - this could be either a
|
||||
account or an object
|
||||
|
||||
Args:
|
||||
sender (Account or Object): The sender of the message.
|
||||
exclude_channel_messages (bool, optional): Only return messages
|
||||
not aimed at a channel (that is, private tells for example)
|
||||
|
||||
Returns:
|
||||
messages (list): List of matching messages
|
||||
QuerySet: Matching messages.
|
||||
|
||||
Raises:
|
||||
CommError: For incorrect sender types.
|
||||
|
||||
"""
|
||||
obj, typ = identify_object(sender)
|
||||
if exclude_channel_messages:
|
||||
# explicitly exclude channel recipients
|
||||
if typ == "account":
|
||||
return list(
|
||||
self.filter(db_sender_accounts=obj, db_receivers_channels__isnull=True).exclude(
|
||||
db_hide_from_accounts=obj
|
||||
)
|
||||
)
|
||||
elif typ == "object":
|
||||
return list(
|
||||
self.filter(db_sender_objects=obj, db_receivers_channels__isnull=True).exclude(
|
||||
db_hide_from_objects=obj
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise CommError
|
||||
if typ == "account":
|
||||
return self.filter(db_sender_accounts=obj).exclude(db_hide_from_accounts=obj)
|
||||
elif typ == "object":
|
||||
return self.filter(db_sender_objects=obj).exclude(db_hide_from_objects=obj)
|
||||
elif typ == "script":
|
||||
return self.filter(db_sender_scripts=obj)
|
||||
else:
|
||||
# get everything, channel or not
|
||||
if typ == "account":
|
||||
return list(self.filter(db_sender_accounts=obj).exclude(db_hide_from_accounts=obj))
|
||||
elif typ == "object":
|
||||
return list(self.filter(db_sender_objects=obj).exclude(db_hide_from_objects=obj))
|
||||
else:
|
||||
raise CommError
|
||||
raise CommError
|
||||
|
||||
def get_messages_by_receiver(self, recipient):
|
||||
"""
|
||||
|
|
@ -209,7 +202,7 @@ class MsgManager(TypedObjectManager):
|
|||
recipient (Object, Account or Channel): The recipient of the messages to search for.
|
||||
|
||||
Returns:
|
||||
messages (list): Matching messages.
|
||||
Queryset: Matching messages.
|
||||
|
||||
Raises:
|
||||
CommError: If the `recipient` is not of a valid type.
|
||||
|
|
@ -217,26 +210,14 @@ class MsgManager(TypedObjectManager):
|
|||
"""
|
||||
obj, typ = identify_object(recipient)
|
||||
if typ == "account":
|
||||
return list(self.filter(db_receivers_accounts=obj).exclude(db_hide_from_accounts=obj))
|
||||
return self.filter(db_receivers_accounts=obj).exclude(db_hide_from_accounts=obj)
|
||||
elif typ == "object":
|
||||
return list(self.filter(db_receivers_objects=obj).exclude(db_hide_from_objects=obj))
|
||||
elif typ == "channel":
|
||||
return list(self.filter(db_receivers_channels=obj).exclude(db_hide_from_channels=obj))
|
||||
return self.filter(db_receivers_objects=obj).exclude(db_hide_from_objects=obj)
|
||||
elif typ == 'script':
|
||||
return self.filter(db_receivers_scripts=obj)
|
||||
else:
|
||||
raise CommError
|
||||
|
||||
def get_messages_by_channel(self, channel):
|
||||
"""
|
||||
Get all persistent messages sent to one channel.
|
||||
|
||||
Args:
|
||||
channel (Channel): The channel to find messages for.
|
||||
|
||||
Returns:
|
||||
messages (list): Persistent Msg objects saved for this channel.
|
||||
|
||||
"""
|
||||
return self.filter(db_receivers_channels=channel).exclude(db_hide_from_channels=channel)
|
||||
|
||||
def search_message(self, sender=None, receiver=None, freetext=None, dbref=None):
|
||||
"""
|
||||
|
|
@ -244,7 +225,7 @@ class MsgManager(TypedObjectManager):
|
|||
one of the arguments must be given to do a search.
|
||||
|
||||
Args:
|
||||
sender (Object or Account, optional): Get messages sent by a particular account or object
|
||||
sender (Object, Account or Script, optional): Get messages sent by a particular sender.
|
||||
receiver (Object, Account or Channel, optional): Get messages
|
||||
received by a certain account,object or channel
|
||||
freetext (str): Search for a text string in a message. NOTE:
|
||||
|
|
@ -255,14 +236,12 @@ class MsgManager(TypedObjectManager):
|
|||
always gives only one match.
|
||||
|
||||
Returns:
|
||||
messages (list or Msg): A list of message matches or a single match if `dbref` was given.
|
||||
Queryset: Message matches.
|
||||
|
||||
"""
|
||||
# unique msg id
|
||||
if dbref:
|
||||
msg = self.objects.filter(id=dbref)
|
||||
if msg:
|
||||
return msg[0]
|
||||
return self.objects.filter(id=dbref)
|
||||
|
||||
# We use Q objects to gradually build up the query - this way we only
|
||||
# need to do one database lookup at the end rather than gradually
|
||||
|
|
@ -275,20 +254,23 @@ class MsgManager(TypedObjectManager):
|
|||
sender_restrict = Q(db_sender_accounts=sender) & ~Q(db_hide_from_accounts=sender)
|
||||
elif styp == "object":
|
||||
sender_restrict = Q(db_sender_objects=sender) & ~Q(db_hide_from_objects=sender)
|
||||
elif styp == 'script':
|
||||
sender_restrict = Q(db_sender_scripts=sender)
|
||||
else:
|
||||
sender_restrict = Q()
|
||||
# filter by receiver
|
||||
receiver, rtyp = identify_object(receiver)
|
||||
if rtyp == "account":
|
||||
receiver_restrict = Q(db_receivers_accounts=receiver) & ~Q(
|
||||
db_hide_from_accounts=receiver
|
||||
)
|
||||
receiver_restrict = (
|
||||
Q(db_receivers_accounts=receiver) & ~Q(db_hide_from_accounts=receiver ))
|
||||
elif rtyp == "object":
|
||||
receiver_restrict = Q(db_receivers_objects=receiver) & ~Q(db_hide_from_objects=receiver)
|
||||
elif rtyp == 'script':
|
||||
receiver_restrict = Q(db_receivers_scripts=receiver)
|
||||
elif rtyp == "channel":
|
||||
receiver_restrict = Q(db_receivers_channels=receiver) & ~Q(
|
||||
db_hide_from_channels=receiver
|
||||
)
|
||||
raise DeprecationWarning(
|
||||
"Msg.objects.search don't accept channel recipients since "
|
||||
"Channels no longer accepts Msg objects.")
|
||||
else:
|
||||
receiver_restrict = Q()
|
||||
# filter by full text
|
||||
|
|
@ -297,7 +279,7 @@ class MsgManager(TypedObjectManager):
|
|||
else:
|
||||
fulltext_restrict = Q()
|
||||
# execute the query
|
||||
return list(self.filter(sender_restrict & receiver_restrict & fulltext_restrict))
|
||||
return self.filter(sender_restrict & receiver_restrict & fulltext_restrict)
|
||||
|
||||
# back-compatibility alias
|
||||
message_search = search_message
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ class Msg(SharedMemoryModel):
|
|||
db_hide_from_objects = models.ManyToManyField(
|
||||
"objects.ObjectDB", related_name="hide_from_objects_set", blank=True
|
||||
)
|
||||
# NOTE: deprecated in 1.0. Not used for channels anymore
|
||||
db_hide_from_channels = models.ManyToManyField(
|
||||
"ChannelDB", related_name="hide_from_channels_set", blank=True
|
||||
)
|
||||
|
|
@ -263,9 +264,8 @@ class Msg(SharedMemoryModel):
|
|||
elif clsname == "ScriptDB":
|
||||
self.db_sender_accounts.remove(sender)
|
||||
|
||||
# receivers property
|
||||
# @property
|
||||
def __receivers_get(self):
|
||||
@property
|
||||
def receivers(self):
|
||||
"""
|
||||
Getter. Allows for value = self.receivers.
|
||||
Returns four lists of receivers: accounts, objects, scripts and channels.
|
||||
|
|
@ -277,8 +277,8 @@ class Msg(SharedMemoryModel):
|
|||
+ list(self.db_receivers_channels.all())
|
||||
)
|
||||
|
||||
# @receivers.setter
|
||||
def __receivers_set(self, receivers):
|
||||
@receivers.setter
|
||||
def receivers(self, receivers):
|
||||
"""
|
||||
Setter. Allows for self.receivers = value.
|
||||
This appends a new receiver to the message.
|
||||
|
|
@ -298,8 +298,8 @@ class Msg(SharedMemoryModel):
|
|||
elif clsname == "ChannelDB":
|
||||
self.db_receivers_channels.add(receiver)
|
||||
|
||||
# @receivers.deleter
|
||||
def __receivers_del(self):
|
||||
@receivers.deleter
|
||||
def receivers(self):
|
||||
"Deleter. Clears all receivers"
|
||||
self.db_receivers_accounts.clear()
|
||||
self.db_receivers_objects.clear()
|
||||
|
|
@ -307,7 +307,6 @@ class Msg(SharedMemoryModel):
|
|||
self.db_receivers_channels.clear()
|
||||
self.save()
|
||||
|
||||
receivers = property(__receivers_get, __receivers_set, __receivers_del)
|
||||
|
||||
def remove_receiver(self, receivers):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -360,22 +360,21 @@ help_entry = create_help_entry
|
|||
|
||||
|
||||
def create_message(
|
||||
senderobj, message, channels=None, receivers=None, locks=None, tags=None, header=None
|
||||
):
|
||||
senderobj, message, receivers=None, locks=None, tags=None,
|
||||
header=None, **kwargs):
|
||||
"""
|
||||
Create a new communication Msg. Msgs represent a unit of
|
||||
database-persistent communication between entites.
|
||||
|
||||
Args:
|
||||
senderobj (Object or Account): The entity sending the Msg.
|
||||
senderobj (Object, Account, Script, str or list): The entity (or
|
||||
entities) sending the Msg. If a `str`, this is the id-string
|
||||
for an external sender type.
|
||||
message (str): Text with the message. Eventual headers, titles
|
||||
etc should all be included in this text string. Formatting
|
||||
will be retained.
|
||||
channels (Channel, key or list): A channel or a list of channels to
|
||||
send to. The channels may be actual channel objects or their
|
||||
unique key strings.
|
||||
receivers (Object, Account, str or list): An Account/Object to send
|
||||
to, or a list of them. May be Account objects or accountnames.
|
||||
receivers (Object, Account or list): An Account/Object to send
|
||||
to, or a list of them.
|
||||
locks (str): Lock definition string.
|
||||
tags (list): A list of tags or tuples `(tag, category)`.
|
||||
header (str): Mime-type or other optional information for the message
|
||||
|
|
@ -387,6 +386,12 @@ def create_message(
|
|||
limit this as desired.
|
||||
|
||||
"""
|
||||
if 'channels' in kwargs:
|
||||
raise DeprecationWarning(
|
||||
"create_message() does not accept 'channel' kwarg anymore "
|
||||
"- channels no longer accept Msg objects."
|
||||
)
|
||||
|
||||
global _Msg
|
||||
if not _Msg:
|
||||
from evennia.comms.models import Msg as _Msg
|
||||
|
|
@ -398,8 +403,6 @@ def create_message(
|
|||
for sender in make_iter(senderobj):
|
||||
new_message.senders = sender
|
||||
new_message.header = header
|
||||
for channel in make_iter(channels):
|
||||
new_message.channels = channel
|
||||
for receiver in make_iter(receivers):
|
||||
new_message.receivers = receiver
|
||||
if locks:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue