Repository: evennia/evennia

Issue:
at_msg_receive hook not firing when channel message received.
#### Brief summary of issue / Description of requested feature:
I have an object that needs to do work when a message is received from a channel.


#### Steps to reproduce the issue / Reasons for adding feature:

1. Create test object with at_msg_receive hook.
2. Add object to a channel
3. send a message on that channel.

#### Error output / Expected result of feature
Nothing happens when the channel sends a message to the object.  verified that the object is listed and highlighted for that specific channel in allcom who

#### Extra information, such as Evennia revision/repo/branch, operating system and ideas for how to solve / implement:

at_msg_receive hook not firing when channel message received.
#### Brief summary of issue / Description of requested feature:
I have an object that needs to do work when a message is received from a channel.


#### Steps to reproduce the issue / Reasons for adding feature:

1. Create test object with at_msg_receive hook.
2. Add object to a channel
3. send a message on that channel.

#### Error output / Expected result of feature
Nothing happens when the channel sends a message to the object.  verified that the object is listed and highlighted for that specific channel in allcom who

#### Extra information, such as Evennia revision/repo/branch, operating system and ideas for how to solve / implement:



Discussion:
OK, so this is an interesting one that @grungies1138 and I figured out together.

First off, `at_msg_receive` for whatever reason has to be used like so:
```
def at_msg_receive(self, text=None, source=None):
    # your code here
```
@grungies1138 was getting an error related to this when messaging the object otherwise.

Next, the reason we're not getting messages is that the Channels expect their subscribed objects to have an `is_connected` property. This property is only used by Accounts - so we end up with an error there. @grungies1138 solved this in an easy way:
```
@lazy_property
def is_connected(self):
    return True
```

Finally, even with this change, you _still_ won't get messages on that object, unless you directly use `Channel.msg(text)`. This is because of [the online() property](https://github.com/evennia/evennia/blob/master/evennia/comms/models.py#L575), which will skip objects that pass `hasattr(obj, 'account')` and also don't have an account, e.g. `obj.account is None`. If one patches that section to be:
```
if hasattr(obj, 'account'):
    if obj.account:
        obj = obj.account
if not obj.is_connected:
    continue
```
then this issue is completely 'fixed' - objects can now receive messages correctly.
OK, so this is an interesting one that @grungies1138 and I figured out together.

First off, `at_msg_receive` for whatever reason has to be used like so:
```
def at_msg_receive(self, text=None, source=None):
    # your code here
```
@grungies1138 was getting an error related to this when messaging the object otherwise.

Next, the reason we're not getting messages is that the Channels expect their subscribed objects to have an `is_connected` property. This property is only used by Accounts - so we end up with an error there. @grungies1138 solved this in an easy way:
```
@lazy_property
def is_connected(self):
    return True
```

Finally, even with this change, you _still_ won't get messages on that object, unless you directly use `Channel.msg(text)`. This is because of [the online() property](https://github.com/evennia/evennia/blob/master/evennia/comms/models.py#L575), which will skip objects that pass `hasattr(obj, 'account')` and also don't have an account, e.g. `obj.account is None`. If one patches that section to be:
```
if hasattr(obj, 'account'):
    if obj.account:
        obj = obj.account
if not obj.is_connected:
    continue
```
then this issue is completely 'fixed' - objects can now receive messages correctly.

Resolving patch:
diff --git a/evennia/comms/models.py b/evennia/comms/models.py
--- a/evennia/comms/models.py
+++ b/evennia/comms/models.py
@@ -584,9 +584,7 @@ def online(self):
         for obj in self.all():
             from django.core.exceptions import ObjectDoesNotExist
             try:
-                if hasattr(obj, 'account'):
-                    if not obj.account:
-                        continue
+                if hasattr(obj, 'account') and obj.account:
                     obj = obj.account
                 if not obj.is_connected:
                     continue
diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py
--- a/evennia/objects/objects.py
+++ b/evennia/objects/objects.py
@@ -206,6 +206,14 @@ def nicks(self):
     def sessions(self):
         return ObjectSessionHandler(self)
 
+    @property
+    def is_connected(self):
+        # we get an error for objects subscribed to channels without this
+        if self.account: # seems sane to pass on the account
+            return self.account.is_connected
+        else:
+            return False
+
     @property
     def has_account(self):
         """
@@ -519,6 +527,7 @@ def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
                     obj.at_msg_send(text=text, to_obj=self, **kwargs)
                 except Exception:
                     logger.log_trace()
+        kwargs["options"] = options
         try:
             if not self.at_msg_receive(text=text, **kwargs):
                 # if at_msg_receive returns false, we abort message to this object
@@ -526,7 +535,6 @@ def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
         except Exception:
             logger.log_trace()
 
-        kwargs["options"] = options
 
         if text and not (isinstance(text, basestring) or isinstance(text, tuple)):
             # sanitize text before sending across the wire


Test patch: