mirror of
https://github.com/evennia/evennia.git
synced 2026-03-17 05:16:31 +01:00
Added a @deluser command and gave it and @boot an option to give a reason for booting/deleting a player Fixed a bug in @dig that confused exit directions in text Small bug fixes /Griatch
398 lines
15 KiB
Python
398 lines
15 KiB
Python
"""
|
|
Comsys functions.
|
|
"""
|
|
import time
|
|
import datetime
|
|
from django.conf import settings
|
|
from django.utils import simplejson
|
|
from src.channels.models import CommChannel, CommChannelMessage, CommChannelMembership
|
|
from src import session_mgr
|
|
from src import ansi
|
|
from src import logger
|
|
from src.imc2.packets import IMC2PacketIceMsgBroadcasted
|
|
from src.imc2.models import IMC2ChannelMapping
|
|
from src.irc.models import IRCChannelMapping
|
|
import src.ansi
|
|
|
|
def plr_get_cdict(session):
|
|
"""
|
|
Returns the users's channel subscription dictionary. Use this instead of
|
|
directly referring to the session object.
|
|
|
|
session: (SessionProtocol) Reference to a player session.
|
|
"""
|
|
return session.channels_subscribed
|
|
|
|
def plr_listening_channel(session, cstr, alias_search=False):
|
|
"""
|
|
Returns a player's listening status for a channel.
|
|
|
|
session: (SessionProtocol) Reference to a player session.
|
|
cstr: (str) The channel name or alias (depending on alias_search).
|
|
alias_search: (bool) If true, search by alias. Else search by full name.
|
|
"""
|
|
return plr_has_channel(session, cstr, alias_search=alias_search,
|
|
return_muted=False)
|
|
|
|
def plr_cname_from_alias(session, calias):
|
|
"""
|
|
Returns a channel name given a channel alias.
|
|
|
|
session: (SessionProtocol) Reference to a player session.
|
|
calias: (str) The channel alias.
|
|
"""
|
|
return plr_get_cdict(session).get(calias, None)[0]
|
|
|
|
def plr_chan_off(session, calias):
|
|
"""
|
|
Turn a channel off for a player.
|
|
|
|
session: (SessionProtocol) Reference to a player session.
|
|
calias: (str) The channel alias.
|
|
"""
|
|
if not plr_listening_channel(session, calias, alias_search=True):
|
|
session.msg("You're already not listening to that channel.")
|
|
return
|
|
else:
|
|
cname = plr_cname_from_alias(session, calias)
|
|
cobj = get_cobj_from_name(cname)
|
|
plr_set_channel_listening(session, calias, False)
|
|
session.msg("You have left %s." % (cname,))
|
|
send_cmessage(cname, "%s has left the channel." % (
|
|
session.get_pobject().get_name(show_dbref=False),))
|
|
|
|
def plr_chan_on(session, calias):
|
|
"""
|
|
Turn a channel on for a player.
|
|
|
|
session: (SessionProtocol) Reference to a player session.
|
|
calias: (str) The channel alias.
|
|
"""
|
|
plr_listening = plr_listening_channel(session, calias, alias_search=True)
|
|
if plr_listening:
|
|
session.msg("You're already listening to that channel.")
|
|
return
|
|
else:
|
|
cname = plr_cname_from_alias(session, calias)
|
|
cobj = get_cobj_from_name(cname)
|
|
send_cmessage(cname, "%s has joined the channel." % (
|
|
session.get_pobject().get_name(show_dbref=False),))
|
|
plr_set_channel_listening(session, calias, True)
|
|
session.msg("You have joined %s." % (cname,))
|
|
|
|
def plr_has_channel(session, cname, alias_search=False, return_muted=False):
|
|
"""
|
|
Is this session subscribed to the named channel?
|
|
|
|
session: (SessionProtocol) Reference to a player session.
|
|
cname: (str) The channel name or alias (depending on alias_search)
|
|
alias_search: (bool) If False, search by full name. Else search by alias.
|
|
return_muted: (bool) Take the user's enabling/disabling of the channel
|
|
into consideration?
|
|
"""
|
|
has_channel = False
|
|
|
|
if alias_search:
|
|
# Search by aliases only.
|
|
cdat = plr_get_cdict(session).get(cname, False)
|
|
# No key match, fail immediately.
|
|
if not cdat:
|
|
return False
|
|
|
|
# If channel status is taken into consideration, see if the user
|
|
# has the channel muted or not.
|
|
if return_muted:
|
|
return True
|
|
else:
|
|
return cdat[1]
|
|
|
|
else:
|
|
# Search by complete channel name.
|
|
chan_list = plr_get_cdict(session).values()
|
|
for chan in chan_list:
|
|
# Check for a name match
|
|
if cname.lower() == chan[0].lower():
|
|
has_channel = True
|
|
|
|
# If channel status is taken into consideration, see if the user
|
|
# has the channel muted or not.
|
|
if return_muted is False and not chan[1]:
|
|
has_channel = False
|
|
break
|
|
|
|
return has_channel
|
|
|
|
def plr_set_channel_listening(session, alias, listening):
|
|
"""
|
|
Enables or disables listening on a particular channel based on the
|
|
user's channel alias.
|
|
|
|
session: (SessionProtocol) A reference to the player session.
|
|
alias: (str) The channel alias.
|
|
listening: (bool) A True or False value to determine listening status.
|
|
"""
|
|
|
|
membership = session.get_pobject().channel_membership_set.get(user_alias=alias)
|
|
membership.is_listening = listening
|
|
membership.save()
|
|
plr_get_cdict(session).get(alias)[1] = listening
|
|
|
|
def plr_add_channel(source_object, alias, channel):
|
|
"""
|
|
Adds a player to a channel via a CommChannelMembership and sets the cached
|
|
cdict value.
|
|
|
|
source_object: (Object) Reference to the object that will be listening.
|
|
alias: (str) The channel alias (also the key in the user's cdict)
|
|
channel: (CommChannel) The channel object to add.
|
|
listening: (bool) A True or False value to determine listening status.
|
|
"""
|
|
membership = CommChannelMembership(channel=channel, listener=source_object,
|
|
user_alias=alias)
|
|
membership.save()
|
|
|
|
sessions = session_mgr.sessions_from_object(source_object)
|
|
for session in sessions:
|
|
plr_get_cdict(session)[alias] = [channel.get_name(), True]
|
|
|
|
def plr_del_channel(source_object, alias):
|
|
"""
|
|
Remove a channel from a session's channel list.
|
|
|
|
source_object: (Object) Reference to the object that will be listening.
|
|
alias: (str) The channel alias (also the key in the user's cdict)
|
|
"""
|
|
membership = source_object.channel_membership_set.get(user_alias=alias)
|
|
membership.delete()
|
|
|
|
sessions = session_mgr.sessions_from_object(source_object)
|
|
for session in sessions:
|
|
del plr_get_cdict(session)[alias]
|
|
|
|
def msg_chan_hist(target_obj, channel_name):
|
|
"""
|
|
Sends a listing of subscribers to a channel given a channel name.
|
|
|
|
target_obj: (Object) The object to send the history listing to.
|
|
channel_name: (str) The channel's full name.
|
|
"""
|
|
cobj = get_cobj_from_name(channel_name)
|
|
hist_list = CommChannelMessage.objects.filter(channel=cobj).order_by('date_sent')
|
|
|
|
# Negative indexing is not currently supported with QuerySet objects.
|
|
# Figure out what the first CommChannelMessage is to return and grab the
|
|
# next 20.
|
|
first_msg = hist_list.count() - 20
|
|
# Prevent a negative index from being called on.
|
|
if first_msg < 0:
|
|
first_msg = 0
|
|
|
|
# Slice and dice, display last 20 messages.
|
|
for entry in hist_list[first_msg:]:
|
|
delta_days = datetime.datetime.now() - entry.date_sent
|
|
days_elapsed = delta_days.days
|
|
if days_elapsed > 0:
|
|
# Message happened more than a day ago, show the date.
|
|
time_str = entry.date_sent.strftime("%m.%d / %H:%M")
|
|
else:
|
|
# Recent message (within last 24 hours), show hour:minute.
|
|
time_str = entry.date_sent.strftime("%H:%M")
|
|
target_obj.emit_to("[%s] %s" % (time_str, entry.message))
|
|
|
|
def msg_cwho(target_obj, channel_name):
|
|
"""
|
|
Sends a listing of subscribers to a channel given a channel name.
|
|
|
|
target_obj: (Object) Send the cwho listing to this object.
|
|
channel_name: (str) The channel's full name.
|
|
"""
|
|
target_obj.emit_to("--- Users Listening to %s ---" % (channel_name,))
|
|
for plr_sess in get_cwho_list(channel_name):
|
|
target_obj.emit_to(plr_sess.get_pobject().get_name(show_dbref=target_obj.sees_dbrefs()))
|
|
target_obj.emit_to("--- End Channel Listeners ---")
|
|
|
|
def get_cwho_list(channel_name, return_muted=False):
|
|
"""
|
|
Get all users on a channel.
|
|
|
|
channel_name: (string) The name of the channel.
|
|
return_muted: (bool) Return those who have the channel muted if True.
|
|
"""
|
|
sess_list = session_mgr.get_session_list()
|
|
return [sess for sess in sess_list if plr_has_channel(sess, channel_name, return_muted)]
|
|
|
|
def load_object_channels(pobject):
|
|
"""
|
|
Parse JSON dict of a user's channel list from their CHANLIST attribute.
|
|
"""
|
|
membership_list = pobject.channel_membership_set.all()
|
|
for membership in membership_list:
|
|
sessions = session_mgr.sessions_from_object(pobject)
|
|
for session in sessions:
|
|
session.channels_subscribed[membership.user_alias] = [membership.channel.name,
|
|
membership.is_listening]
|
|
|
|
def send_cmessage(channel, message, show_header=True, from_external=None):
|
|
"""
|
|
Sends a message to all players on the specified channel.
|
|
|
|
channel: (string or CommChannel) Name of channel or a CommChannel object.
|
|
message: (string) Message to send.
|
|
show_header: (bool) If False, don't prefix message with the channel header.
|
|
from_external: (string/None)
|
|
Can be None, 'IRC' or 'IMC2'. The sending functions of the
|
|
respective protocol sets this flag, otherwise it should
|
|
be None; it allows for piping messages between protocols
|
|
without accidentally also echoing it back to where it came from.
|
|
"""
|
|
if isinstance(channel, unicode) or isinstance(channel, str):
|
|
# If they've passed a string as the channel argument, look up the
|
|
# correct channel object.
|
|
try:
|
|
channel_obj = get_cobj_from_name(channel)
|
|
except:
|
|
logger.log_errmsg("send_cmessage(): Can't find channel: %s" % channel)
|
|
return
|
|
else:
|
|
# Else, assume that it's a channel object and skip re-querying for
|
|
# the channel.
|
|
channel_obj = channel
|
|
|
|
final_message = message
|
|
if show_header == True:
|
|
final_message = "%s %s" % (channel_obj.ansi_name, message)
|
|
|
|
for user in get_cwho_list(channel_obj.name, return_muted=False):
|
|
user.msg(final_message)
|
|
|
|
chan_message = CommChannelMessage()
|
|
chan_message.channel = channel_obj
|
|
chan_message.message = final_message
|
|
chan_message.save()
|
|
|
|
#pipe to external protocols
|
|
if from_external:
|
|
send_cexternal(channel_obj.name, message,
|
|
from_external=from_external)
|
|
|
|
def get_all_channels():
|
|
"""
|
|
Returns all channel objects.
|
|
"""
|
|
return CommChannel.objects.all()
|
|
|
|
def get_cobj_from_name(cname):
|
|
"""
|
|
Returns the channel's object when given a name.
|
|
"""
|
|
return CommChannel.objects.get(name=cname)
|
|
|
|
def create_channel(name, owner, description=None):
|
|
"""
|
|
Create a new channel.
|
|
name: (string) Name of the new channel
|
|
owner: (Object) Object that owns the channel
|
|
"""
|
|
new_chan = CommChannel()
|
|
new_chan.name = ansi.parse_ansi(name, strip_ansi=True)
|
|
new_chan.ansi_name = "[%s]" % (ansi.parse_ansi(name),)
|
|
new_chan.set_owner(owner)
|
|
new_chan.description = description
|
|
new_chan.save()
|
|
return new_chan
|
|
|
|
def cname_search(search_text, exact=False):
|
|
"""
|
|
Searches for a particular channel name. Returns a QuerySet with the
|
|
results.
|
|
|
|
exact: (bool) Do an exact (case-insensitive) name match if true.
|
|
"""
|
|
if exact:
|
|
return CommChannel.objects.filter(name__iexact=search_text)
|
|
else:
|
|
return CommChannel.objects.filter(name__istartswith=search_text)
|
|
|
|
def cemit_mudinfo(message):
|
|
"Send to mud info channel This is "
|
|
send_cmessage(settings.COMMCHAN_MUD_INFO,
|
|
'Info: %s' % message)
|
|
|
|
def send_cexternal(cname, cmessage, caller=None, from_external=None):
|
|
"""
|
|
This allows external protocols like IRC and IMC to send to a channel
|
|
while also echoing to each other. This used by channel-emit functions
|
|
to transparently distribute channel sends to external protocols.
|
|
|
|
cname - name of evennia channel sent to
|
|
cmessage - message sent (should be pre-formatted already)
|
|
caller - a player object sending the message (not present for IRC<->IMC comms)
|
|
from_external - which protocol sent the emit.
|
|
Currently supports 'IRC' and 'IMC2' or None
|
|
(this avoids emits echoing back to themselves). If
|
|
None, it is assumed the message comes from within Evennia
|
|
and all mapped external channels will be notified.
|
|
"""
|
|
|
|
if settings.IMC2_ENABLED and from_external != "IMC2":
|
|
#map a non-IMC emit to the IMC network
|
|
|
|
# Look for IMC2 channel maps. If one is found, send an ice-msg-b
|
|
# packet to the network.
|
|
#logger.msg_infomsg("caller found: %s (%s). Msg: %s" % (caller,from_external,cmessage))
|
|
|
|
imccaller = caller
|
|
imccmessage = cmessage
|
|
if not caller:
|
|
if from_external in ['IRC']:
|
|
#try to guess the caller's name from the message
|
|
#always on form name@#channel: msg
|
|
imccaller, imccmessage = cmessage.split("@",1)
|
|
#throw away IRC channel name; IMC users need not know it
|
|
imccmessage = (imccmessage.split(":",1)[1]).strip()
|
|
#logger.msg_infomsg("caller: %s cmessage: %s" % (imccaller, imccmessage))
|
|
else:
|
|
imccaller = "*"
|
|
|
|
try:
|
|
from src.imc2.connection import IMC2_PROTOCOL_INSTANCE
|
|
map = IMC2ChannelMapping.objects.get(channel__name=cname)
|
|
packet = IMC2PacketIceMsgBroadcasted(map.imc2_server_name,
|
|
map.imc2_channel_name,
|
|
imccaller,
|
|
imccmessage)
|
|
IMC2_PROTOCOL_INSTANCE.send_packet(packet)
|
|
except IMC2ChannelMapping.DoesNotExist:
|
|
# No map found, do nothing.
|
|
pass
|
|
|
|
if settings.IRC_ENABLED and from_external != "IRC":
|
|
# Map a non-IRC emit to IRC channels
|
|
|
|
# Look for IRC channel maps. If found, echo cmessage to the
|
|
# IRC channel.
|
|
|
|
if caller:
|
|
#message comes from a local channel
|
|
caller = caller.get_name(show_dbref=False)
|
|
cmessage = "[%s] %s: %s" % (cname, caller, cmessage)
|
|
else:
|
|
#message from IMC2; name is part of cmessage
|
|
cmessage = "[%s] %s" % (cname, cmessage)
|
|
|
|
try:
|
|
#this fails with a DoesNotExist if the channel is not mapped.
|
|
from src.irc.connection import IRC_CHANNELS
|
|
mapping = IRCChannelMapping.objects.filter(channel__name=cname)
|
|
#strip the message of ansi characters.
|
|
cmessage = ansi.clean_ansi(cmessage)
|
|
for mapp in mapping:
|
|
mapped_irc = filter(lambda c: c.factory.channel==mapp.irc_channel_name,
|
|
IRC_CHANNELS)
|
|
for chan in mapped_irc:
|
|
chan.send_msg(cmessage.encode("utf-8"))
|
|
except IRCChannelMapping.DoesNotExist:
|
|
#no mappings. Ignore
|
|
pass
|
|
|
|
|